Skip to content

Commit 4ac0aee

Browse files
Merging updates from master (#118)
* Implementation of #104 * Update README.md * Update GPFix.java (#110) Modify fitness calculation to account for edge case when passing_tests = 0 and failing_tests>0 * Update README.md Added example for MergeProfilerFiles * Update MergeProfilerFiles.java Correction to javadocs for arguments --------- Co-authored-by: Prasham Jadhwani <91847148+PrashamJ94@users.noreply.github.com>
1 parent c5d7e70 commit 4ac0aee

4 files changed

Lines changed: 206 additions & 11 deletions

File tree

README.md

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,19 @@ Please cite the following papers, if you use Gin for academic purposes: <br>
2121
- release 1: ["GI in No Time"](https://github.com/gintool/gin/blob/master/doc/gin.pdf), David R. White, 3rd
2222
International GI Workshop, GECCO Companion Material Proceedings, 2017.
2323

24-
Extensions:
24+
Extensions and specific parts of Gin:
2525

26-
Please cite the following paper, if using the edits from
27-
the [insert](https://github.com/gintool/gin/tree/master/src/main/java/gin/edit/insert) folder:
26+
- "Standard" Statement/Line Edits from the [statement](https://github.com/gintool/gin/tree/master/src/main/java/gin/edit/statement) and [line](https://github.com/gintool/gin/tree/master/src/main/java/gin/edit/line) folders:
27+
["Program Transformation Landscapes for Automated Program Modification Using Gin"](https://link.springer.com/article/10.1007/s10664-023-10344-5), Petke, J., Alexander, B., Barr, E.T., Brownlee, A. E. I., Wagner, M. & White, D.R., vol 28. 2023.
28+
- Edits from the [insert](https://github.com/gintool/gin/tree/master/src/main/java/gin/edit/insert) folder:
2829
["Injecting Shortcuts for Faster Running Java Code"](https://ieeexplore.ieee.org/document/9185708), Alexander E. I.
2930
Brownlee, Justyna Petke, Anna F. Rasburn, CEC 2020.
30-
31-
Please cite the following paper, if using Regression Test Selection (RTS) strategies:
31+
- Edits from the [llm](https://github.com/gintool/gin/tree/llm/src/main/java/gin/edit/llm) branch:
32+
["Enhancing Genetic Improvement Mutations Using Large Language Models"](https://link.springer.com/chapter/10.1007/978-3-031-48796-5_13), Alexander E. I. Brownlee, James Callan, Karine Even-Mendoza, Alina Geiger, Carol Hanna, Justyna Petke, Federica Sarro and Dominik Sobania. International Symposium on Search Based Software Engineering 2023. LNCS 14415.
33+
- Regression Test Selection (RTS) strategies:
3234
["Enhancing Genetic Improvement of Software with Regression Test Selection"](https://doi.org/10.1109/ICSE43902.2021.00120), Giovani Guizzo, Justyna Petke, Federica
3335
Sarro, Mark Harman, ICSE 2021.
36+
- Profiler and associated tools: ["Comparing Apples and Oranges? Investigating the Consistency of CPU and Memory Profiler Results Across Multiple Java Versions"](doi.org/10.1007/s10515-024-00423-2), Watkinson, M., Brownlee, A.E.I. Automated Software Engineering vol 31. 2024.
3437

3538
## The Gin Design Philosophy
3639

@@ -205,6 +208,23 @@ unexpected hard-coded dependencies.
205208

206209
A full example with an existing Maven project is given further below.
207210

211+
### Multiple profiling runs
212+
213+
As sampler profiling is a stochastic process, it is also worth performing repeat runs, ideally with a reboot between runs. We have provided a tool to merge multiple profiler CSVs into a single file: gin.util.analysis.MergeProfilerFiles. This will retain only hot methods appearing in more than a specific fraction of the repeats, and takes the union of all unit tests observed as calling a given hot method. If, for example, you were to run the following bash script:
214+
```
215+
#!/bin/bash
216+
projectnameforgin='spatial4j'
217+
218+
for i in {1..20}; do
219+
java -cp ../gin/build/gin.jar gin.util.Profiler -r 1 -p $projectnamforgin -d ./ -h ~/.sdkman/candidates/maven/current/ -o $projectnameforgin.Profiler_output_$i.csv &> $projectnameforgin.Profiler_stdoutstderr_$i.txt
220+
done
221+
```
222+
you will get a series of profiler files like spatial4j.Profiler_output_1.csv, spatial4j.Profiler_output_2.csv, etc. To aggregate them, do this:
223+
```
224+
java -cp ../gin/build/gin.jar gin.util.analysis.MergeProfilerFiles -if "spatial4j.Profiler_output_*.csv" -of spatial4j.Profiler_aggregated_output.csv
225+
```
226+
and you'll get a single file spatial4j.Profiler_aggregated_output.csv. By default this will contain the intersection of hot methods found across all profile runs, with each method having the union of calling unit tests discovered for it.
227+
208228
## Automated test case generation for Maven and Gradle projects
209229

210230
Gin uses [EvoSuite](http://www.evosuite.org/) to generate test cases automatically. Make sure test class file are
@@ -270,6 +290,8 @@ Assuming EvoSuite tests were generated and original tests not removed:
270290
```
271291
java -cp build/gin.jar:testgeneration/evosuite-1.0.6.jar gin.util.RandomSampler -d examples/maven-simple -p my-app -m examples/maven-simple/example_profiler_results.csv -h <path_to_mavenHome>
272292
```
293+
The CSV written out by RandomSampler contains one line per unit test per patch. The utility gin.util.analysis.AggregateRandomSamplerOutput can be used to aggregate these results to one line per patch, with summary statistics indicating test pass rates and run times.
294+
273295

274296
Gin also offers an implementation of the multi-objective algorithm NSGA-II for improving the execution time and memory
275297
consumption of software.

src/main/java/gin/util/GPFix.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,10 @@ private void setup(UnitTestResultSet results) {
135135
failing += 1;
136136
}
137137
}
138+
if (passing == 0 && failing>0){this.multiplier = 1;}
139+
else{
138140
this.multiplier = (failing > 0) ? passing * WEIGHT / failing : 0;
141+
}
139142
Logger.info("Currently failing tests: " + failing);
140143
Logger.info("Currently passing tests (i.e., current fitness): " + passing);
141144
this.targetFitness = passing + (this.multiplier * failing);

src/main/java/gin/util/analysis/AggregateRandomSamplerOutput.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,10 @@
22

33
import java.io.File;
44
import java.io.FileNotFoundException;
5-
import java.io.FileOutputStream;
65
import java.io.FileReader;
76
import java.io.FileWriter;
87
import java.io.IOException;
9-
import java.util.ArrayList;
108
import java.util.HashMap;
11-
import java.util.List;
129
import java.util.Map;
1310

1411
import org.pmw.tinylog.Logger;
@@ -23,17 +20,18 @@
2320
import com.sampullara.cli.Args;
2421
import com.sampullara.cli.Argument;
2522

26-
2723
/**
2824
* RS output is one line per test
29-
* This aggregates to one line per edit; with a count of test passes, total tests run, and total run time
25+
* This tool aggregates to one line per edit; with a count of test passes, total tests run, and total run time
3026
*/
3127
public class AggregateRandomSamplerOutput {
3228

33-
@Argument(alias = "f", description = "Input: a file output by RandomSampler", required = true)
29+
@Argument(alias = "if", description = "Input file: a CSV file output by RandomSampler", required = true)
3430
protected File inputFile;
3531

3632
public static void main(String[] args) {
33+
Logger.info("Random Sampler output is one line per test.");
34+
Logger.info("This tool aggregates to one line per edit; with a count of test passes, total tests run, and total run time.");
3735
AggregateRandomSamplerOutput a = new AggregateRandomSamplerOutput(args);
3836
a.process();
3937
}
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
package gin.util.analysis;
2+
import java.io.File;
3+
import java.io.FileReader;
4+
import java.io.FileWriter;
5+
import java.io.FileFilter;
6+
import java.io.IOException;
7+
import java.util.ArrayList;
8+
import java.util.Arrays;
9+
import java.util.Collections;
10+
import java.util.Comparator;
11+
import java.util.HashMap;
12+
import java.util.List;
13+
import java.util.Map;
14+
import java.util.Set;
15+
import java.util.TreeSet;
16+
17+
import org.apache.commons.io.FileUtils;
18+
import org.apache.commons.io.filefilter.WildcardFileFilter;
19+
import org.pmw.tinylog.Logger;
20+
21+
import com.opencsv.CSVReader;
22+
import com.opencsv.CSVReaderBuilder;
23+
import com.opencsv.CSVWriterBuilder;
24+
import com.opencsv.ICSVWriter;
25+
import com.sampullara.cli.Args;
26+
import com.sampullara.cli.Argument;
27+
28+
29+
public class MergeProfilerFiles {
30+
31+
@Argument(alias = "d", description = "Root directory for filename patterns; defaults to current dir", required = false)
32+
protected String dir = ".";
33+
34+
@Argument(alias = "if", description = "Comma separated list of filename patterns, e.g. spark.Profiler_output_*.csv or spark.Profiler_output_1.csv,spark.Profiler_output_2.csv,spark.Profiler_output_3.csv", required = true)
35+
protected String inputFiles;
36+
37+
@Argument(alias = "of", description = "Output: e.g. spark.Profiler_output.csv", required = true)
38+
protected File outputFile;
39+
40+
@Argument(alias = "n", description = "Only methods in at least n repeats will be included; defaults to the number of files (so keep only the intersection of hot methods)", required = false)
41+
protected int n = -1;
42+
43+
44+
// this will
45+
// only methods in at least n repeats will be included
46+
// count is total
47+
// rank is based on total of counts
48+
// tests include all seen over the files (union)
49+
50+
public static void main(String[] args) {
51+
Logger.info("A tool to merge Gin profiler files.");
52+
Logger.info("Only methods in at least n repeats will be included");
53+
Logger.info("n defaults to the number of files (so keep only the intersection of hot methods)");
54+
Logger.info("In the output, 'count' is sum of counts across all repeat runs; 'rank' is based on the aggregated count");
55+
Logger.info("Tests for each method are all those seen over the input files (union)");
56+
57+
MergeProfilerFiles m = new MergeProfilerFiles(args);
58+
try {
59+
m.mergeProfiles();
60+
} catch (IOException e) {
61+
Logger.error("IOException processing files.");
62+
Logger.error(e);
63+
}
64+
}
65+
66+
public MergeProfilerFiles(String[] args) {
67+
Args.parseOrExit(this, args);
68+
}
69+
70+
71+
public void mergeProfiles() throws IOException {
72+
73+
// figure out the filenames
74+
List<File> inputs = new ArrayList<>();
75+
76+
for (String pattern : inputFiles.split(",")) {
77+
File[] files = new File(dir).listFiles((FileFilter)new WildcardFileFilter(pattern));
78+
inputs.addAll(Arrays.asList(files));
79+
}
80+
Collections.sort(inputs);
81+
82+
if (n < 0) {
83+
n = inputs.size();
84+
} else {
85+
n = Math.min(n, inputs.size());
86+
}
87+
88+
Logger.info("Found " + inputs.size() + " profiler files. Keeping hot methods appearing in at least " + n + " files.");
89+
90+
Map<String, ProfiledMethod> methods = new HashMap<String, ProfiledMethod>();
91+
String project = "";
92+
93+
for (File input : inputs) {
94+
CSVReader inCSV = new CSVReader(new FileReader(input));
95+
96+
inCSV.skip(1);
97+
for (String[] s : inCSV) {
98+
// cols are: Project,Rank,Method,Count,Tests
99+
project = s[0];
100+
ProfiledMethod pm = methods.get(s[2]);
101+
if (pm == null) {
102+
pm = new ProfiledMethod(s[2]);
103+
methods.put(s[2], pm);
104+
}
105+
pm.counts.add(Integer.parseInt(s[3]));
106+
String[] tests = s[4].split(",");
107+
pm.tests.addAll(Arrays.asList(tests));
108+
}
109+
}
110+
111+
List<ProfiledMethod> sortedMethods = new ArrayList<>();
112+
for (ProfiledMethod pm : methods.values()) {
113+
if (pm.counts.size() == n) {
114+
sortedMethods.add(pm);
115+
}
116+
}
117+
118+
Collections.sort(sortedMethods, new Comparator<ProfiledMethod>() {
119+
@Override
120+
public int compare(ProfiledMethod arg0, ProfiledMethod arg1) {
121+
return Integer.compare(arg1.getTotalCount(), arg0.getTotalCount());
122+
}
123+
});
124+
125+
ICSVWriter writer = new CSVWriterBuilder(new FileWriter(outputFile)).build();
126+
writer.writeNext(new String[] {"Project","Rank","Method","Count","Tests"});
127+
128+
int i = 1;
129+
for (ProfiledMethod pm : sortedMethods) {
130+
writer.writeNext(new String[] {
131+
project,
132+
Integer.toString(i++),
133+
pm.methodSignature,
134+
Integer.toString(pm.getTotalCount()),
135+
pm.getCSVTests()}
136+
);
137+
}
138+
writer.close();
139+
140+
141+
Logger.info("All done.");
142+
}
143+
144+
145+
private static class ProfiledMethod {
146+
List<Integer> counts = new ArrayList<>();
147+
Set<String> tests = new TreeSet<>();
148+
String methodSignature;
149+
public ProfiledMethod(String methodSignature) {
150+
this.methodSignature = methodSignature;
151+
}
152+
153+
public int getTotalCount() {
154+
int sum = 0;
155+
for (Integer i : counts) {
156+
sum += i;
157+
}
158+
return sum;
159+
}
160+
161+
public String getCSVTests() {
162+
String rval = "";
163+
for (String s : tests) {
164+
if (!rval.isEmpty()) {
165+
rval += ",";
166+
}
167+
rval += s;
168+
}
169+
return rval;
170+
}
171+
}
172+
}

0 commit comments

Comments
 (0)