-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
cbfddaf
commit 06bd5c7
Showing
8 changed files
with
508 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
# An AspectJ-based API-usage tracer | ||
|
||
The primary and only goal is to log how often the methods in the public API of a specific project are called at runtime. | ||
Calls are written directly to a CSV file. Calls need to be counted afterwards. | ||
The tracer can be integrated (relatively) easily | ||
into an existing Maven or Gradle build. | ||
|
||
## Installing | ||
|
||
Run `mvn clean install -DskipTests` in this directory. This installs the tracer to your local | ||
m2 repo, and it should be usable from then on. | ||
|
||
## Tracing a project | ||
|
||
### For Maven | ||
|
||
Enabling the tracing for an existing project requires some modifications to the `pom.xml` build file and | ||
project structure. | ||
|
||
- Step 1: add an aop.xml file to `src/test/resources/META-INF`. The content can be like below. Replace the | ||
log4j stuff in the example with a package name pattern that makes sense for the lib you want to trace. | ||
|
||
```xml | ||
<aspectj> | ||
<!-- <weaver options="-verbose -showWeaveInfo -debug"> --> | ||
<weaver> | ||
<include within="org.apache.logging.log4j..**"/> | ||
<include within="ch.uzh.ifi.seal.dynamicanalyzer.LogAspect"/> | ||
<!--<exclude within="org.apache.logging.log4j.osgi..**" />--> | ||
</weaver> | ||
<aspects> | ||
<aspect name="ch.uzh.ifi.seal.dynamicanalyzer.LogAspect"/> | ||
</aspects> | ||
</aspectj> | ||
``` | ||
|
||
- Step 2: Add some dependencies to `pom.xml`. You will need to add the following dependencies: | ||
|
||
```xml | ||
<dependency> | ||
<groupId>hopper</groupId> | ||
<artifactId>aspectj-tracer</artifactId> | ||
<version>0.0.1-SNAPSHOT</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.aspectj</groupId> | ||
<artifactId>aspectjrt</artifactId> | ||
<version>1.8.10</version> | ||
</dependency> | ||
``` | ||
|
||
- Step 3: Enable LTW. The following assumes you want to trace unit test execution. In that case add | ||
the following to your surefire configuration: | ||
|
||
```xml | ||
<configuration> | ||
<argLine> | ||
-javaagent:$HOME/.m2/repository/org/aspectj/aspectjweaver/1.8.10/aspectjweaver-1.8.10.jar -Dtracer.file=output.csv -Dtracer.projectname=testproject -Dtracer.libname=log4j2 | ||
</argLine> | ||
</configuration> | ||
``` | ||
|
||
(replace $HOME with your home directory, and the other parameters as you want, see below) | ||
|
||
### For Gradle | ||
|
||
For Gradle the process is *almost* the same. | ||
|
||
- Step 1: the same as for Mvn (see above). | ||
|
||
- Step 2: add repository mavenLocal if not used: | ||
```gradle | ||
repositories { | ||
mavenLocal() | ||
} | ||
``` | ||
|
||
- Step 3: Add some dependencies to `build.gradle`. You will need to add the following dependencies: | ||
|
||
```gradle | ||
testCompile("org.aspectj:aspectjrt:1.8.10") | ||
testCompile("org.aspectj:aspectjweaver:1.8.10") | ||
testCompile("hopper:aspectj-tracer:0.0.1-SNAPSHOT") | ||
``` | ||
|
||
- Step 4: Enable LTW. The following assumes you want to trace unit test execution. In that case add | ||
the following to your Gradle test plugin configuration: | ||
|
||
```gradle | ||
jvmArgs '-javaagent:$HOME/.m2/repository/org/aspectj/aspectjweaver/1.8.10/aspectjweaver-1.8.10.jar' | ||
systemProperty 'tracer.file', 'output.csv' | ||
systemProperty 'tracer.projectname', 'reveno' | ||
systemProperty 'tracer.libname', 'protostuff' | ||
``` | ||
|
||
## Parameters | ||
|
||
The tracer accepts 3 parameters as environment variables. You can either set them via | ||
an argline in the `pom.xml` as above, but you could also export them before launching the app, or give them | ||
as parameter to your Maven or Gradle build (`-Dtracer.file=output.csv`) | ||
|
||
- "tracer.file" is the name of the output file that the results will be written to | ||
- "tracer.projectname" is the project name to use in the CSV file | ||
- "tracer.libname" is the library name to use in the CSV file | ||
|
||
|
||
## Other things of import | ||
|
||
The way the tracer is currently implemented is *slow*. Do not be puzzled if it *significantly* slows down | ||
unit test execution. Some builds might even break because of that. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
# import pandas | ||
import sys | ||
|
||
# usage: python aggregate_traces logfile outfile | ||
|
||
# Version 1 - with Pandas | ||
|
||
# df = pandas.read_csv(sys.argv[0]) | ||
# outfile = sys.argv[1] | ||
# lib = df.LIB[0] | ||
# project = df.PROJECT[0] | ||
# | ||
# with open(outfile, 'w') as f: | ||
# f.write("LIB;PROJECT;METHOD;COUNT\n") | ||
# for method in df.METHOD.unique(): | ||
# count = len(df[df.METHOD == method]) | ||
# f.write("%s;%s;%s;%d\n" % (lib, project, method, count)) | ||
|
||
# Version 2 - linear time | ||
|
||
def parse_file(file): | ||
with open(file, 'r') as infile: | ||
methods = {} | ||
lib = '' | ||
project = '' | ||
for line in infile: | ||
try: | ||
lib, project, method = line.strip().split(';') | ||
if lib == 'LIB': | ||
continue # ignore header | ||
if method in methods: | ||
methods[method] += 1 | ||
else: | ||
methods[method] = 1 | ||
except: | ||
print('Ignoring exception while parsing\n') | ||
return (lib, project, methods) | ||
|
||
def append_to_results(results, lib, project, methods): | ||
if lib not in results: | ||
results[lib] = {} | ||
results[lib][project] = methods | ||
return results | ||
|
||
|
||
|
||
outfile = sys.argv[1] | ||
total_results = {} | ||
for f in sys.argv[2:]: | ||
print("Parsing %s" % f) | ||
lib, project, methods = parse_file(f) | ||
print("Finished reading %s" % f) | ||
append_to_results(total_results, lib, project, methods) | ||
print("Finished reading all") | ||
with open(outfile, 'w') as outfile: | ||
outfile.write('LIB;PROJECT;METHOD;COUNT\n') | ||
for lib in total_results: | ||
for project in total_results[lib]: | ||
for method, count in total_results[lib][project].items(): | ||
outfile.write("%s;%s;%s;%d\n" % (lib, project, method, count)) | ||
print('Finished writing\n') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# import pandas | ||
import sys | ||
|
||
def parse_file(file): | ||
parsed = {} | ||
with open(file, 'r') as infile: | ||
for line in infile: | ||
try: | ||
lib, project, method, count = line.strip().split(';') | ||
if lib == 'LIB': | ||
continue # ignore header | ||
if lib not in parsed: | ||
parsed[lib] = {} | ||
if project not in parsed[lib]: | ||
parsed[lib][project] = {} | ||
parsed[lib][project][method] = int(count) | ||
except: | ||
print('Ignoring exception while parsing\n') | ||
return parsed | ||
|
||
def get_all_methods(ranking): | ||
methods = [] | ||
for project in ranking: | ||
for method in ranking[project]: | ||
if method not in methods: | ||
methods += [method] | ||
return methods | ||
|
||
def weights_for_project(methods): | ||
result = {} | ||
_sum = sum(methods.values()) | ||
for method in methods: | ||
result[method] = methods[method] / float(_sum) | ||
return result | ||
|
||
def select_top_n(ranking, n): | ||
aggregated_weights = {} | ||
methods = get_all_methods(ranking) | ||
for method in methods: | ||
agg = 0 | ||
for project in ranking: | ||
if method in ranking[project]: | ||
agg += ranking[project][method] | ||
aggregated_weights[method] = agg | ||
sorted_list = sorted(aggregated_weights.items(), key=lambda x: -1 * x[1]) | ||
return sorted_list[:n+1] | ||
|
||
infile = sys.argv[1] | ||
top_n = int(sys.argv[2]) | ||
data = parse_file(infile) | ||
|
||
for lib in data: | ||
ranking = {} | ||
for project in data[lib]: | ||
ranking[project] = weights_for_project(data[lib][project]) | ||
top = select_top_n(ranking, top_n) | ||
print("Ranking of methods for %s:" % lib) | ||
for t in top: | ||
print("%s (weight %0.5f)" % (t[0], t[1])) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
<groupId>hopper</groupId> | ||
<artifactId>aspectj-tracer</artifactId> | ||
<version>0.0.1-SNAPSHOT</version> | ||
<packaging>jar</packaging> | ||
<dependencies> | ||
<dependency> | ||
<groupId>org.aspectj</groupId> | ||
<artifactId>aspectjrt</artifactId> | ||
<version>1.8.10</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.aspectj</groupId> | ||
<artifactId>aspectjweaver</artifactId> | ||
<version>1.8.10</version> | ||
</dependency> | ||
</dependencies> | ||
<build> | ||
<plugins> | ||
<!-- | ||
<plugin> | ||
<groupId>org.codehaus.mojo</groupId> | ||
<artifactId>aspectj-maven-plugin</artifactId> | ||
<version>1.4</version> | ||
<configuration> | ||
<source>1.8</source> | ||
<target>1.8</target> | ||
<complianceLevel>1.8</complianceLevel> | ||
<verbose>true</verbose> | ||
<weaveWithAspectsInMainSourceFolder>true</weaveWithAspectsInMainSourceFolder> | ||
</configuration> | ||
<executions> | ||
<execution> | ||
<configuration> | ||
<XnoInline>true</XnoInline> | ||
</configuration> | ||
<goals> | ||
<goal>compile</goal> | ||
<goal>test-compile</goal> | ||
</goals> | ||
</execution> | ||
</executions> | ||
<dependencies> | ||
<dependency> | ||
<groupId>org.aspectj</groupId> | ||
<artifactId>aspectjrt</artifactId> | ||
<version>1.8.10</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.aspectj</groupId> | ||
<artifactId>aspectjtools</artifactId> | ||
<version>1.8.10</version> | ||
</dependency> | ||
</dependencies> | ||
</plugin> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-surefire-plugin</artifactId> | ||
<version>2.9</version> | ||
<configuration> | ||
<argLine> | ||
-javaagent:/Users/philipp/.m2/repository/org/aspectj/aspectjweaver/1.8.10/aspectjweaver-1.8.10.jar -Dtracer.file=output.csv -Dtracer.projectname=testproject -Dtracer.libname=log4j2 | ||
</argLine> | ||
</configuration> | ||
</plugin> | ||
<plugin> | ||
<groupId>org.codehaus.mojo</groupId> | ||
<artifactId>exec-maven-plugin</artifactId> | ||
<executions> | ||
<execution> | ||
<goals> | ||
<goal>exec</goal> | ||
</goals> | ||
</execution> | ||
</executions> | ||
<configuration> | ||
<executable>java</executable> | ||
<arguments> | ||
<argument>-javaagent:/Users/philipp/.m2/repository/org/aspectj/aspectjweaver/1.8.10/aspectjweaver-1.8.10.jar</argument> | ||
<argument>-classpath</argument> | ||
<classpath /> | ||
<argument>ch.uzh.ifi.seal.dynamicanalyzer.Testclass</argument> | ||
</arguments> | ||
</configuration> | ||
</plugin> | ||
--> | ||
</plugins> | ||
</build> | ||
</project> |
54 changes: 54 additions & 0 deletions
54
src/main/java/ch/uzh/ifi/seal/dynamicanalyzer/CsvFileAppender.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package ch.uzh.ifi.seal.dynamicanalyzer; | ||
|
||
import java.io.BufferedWriter; | ||
import java.io.File; | ||
import java.io.FileWriter; | ||
import java.io.IOException; | ||
import java.util.Formatter; | ||
|
||
public class CsvFileAppender { | ||
|
||
private String filename = System.getProperty("tracer.file"); | ||
private String libname = System.getProperty("tracer.libname"); | ||
private String projectname = System.getProperty("tracer.projectname"); | ||
private BufferedWriter appender = null; | ||
|
||
private static CsvFileAppender instance = null; | ||
|
||
private CsvFileAppender() throws IOException { | ||
if(!new File(this.filename).exists()) { | ||
appender = new BufferedWriter(new FileWriter(filename)); | ||
writeFileHeader(appender); | ||
} else { | ||
appender = new BufferedWriter(new FileWriter(filename, true)); | ||
} | ||
} | ||
|
||
public synchronized static CsvFileAppender getInstance() throws IOException { | ||
if(instance == null) | ||
instance = new CsvFileAppender(); | ||
return instance; | ||
} | ||
|
||
public synchronized void addInvocationEntry(String fullMethodName) throws IOException { | ||
|
||
writeEntry(appender, libname, projectname, fullMethodName); | ||
|
||
} | ||
|
||
private void writeEntry(BufferedWriter writer, String lib, String project, | ||
String method) throws IOException { | ||
StringBuilder sb = new StringBuilder(); | ||
Formatter formatter = new Formatter(sb); | ||
formatter.format("%s;%s;%s\n", lib, project, method); | ||
writer.write(sb.toString()); | ||
formatter.close(); | ||
} | ||
|
||
private void writeFileHeader(BufferedWriter writer) throws IOException { | ||
String header = "LIB;PROJECT;METHOD\n"; | ||
writer.write(header); | ||
writer.flush(); | ||
} | ||
|
||
} |
Oops, something went wrong.