Skip to content

Commit 1cd89b4

Browse files
author
Marcel Sauer
committed
lots of changes and docs.
1 parent 87d2e0e commit 1cd89b4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+534
-609
lines changed

.travis.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
language: java
2+
jdk:
3+
- oraclejdk8
4+
- oraclejdk9
5+
- openjdk8
6+
- oraclejdk7
7+
- openjdk7
8+
- openjdk6

LICENSE.txt

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
1-
The MIT License (MIT)
1+
MIT License
22

3-
Copyright (c) 2015 Marcel Sauer
3+
Copyright (c) 2018 Marcel Sauer <niesfisch79@gmail.com>
44

5-
Permission is hereby granted, free of charge, to any person obtaining a copy
6-
of this software and associated documentation files (the "Software"), to deal
7-
in the Software without restriction, including without limitation the rights
8-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9-
copies of the Software, and to permit persons to whom the Software is
10-
furnished to do so, subject to the following conditions:
5+
Permission is hereby granted, free of charge, to any person obtaining
6+
a copy of this software and associated documentation files (the
7+
"Software"), to deal in the Software without restriction, including
8+
without limitation the rights to use, copy, modify, merge, publish,
9+
distribute, sublicense, and/or sell copies of the Software, and to
10+
permit persons to whom the Software is furnished to do so, subject to
11+
the following conditions:
1112

12-
The above copyright notice and this permission notice shall be included in all
13-
copies or substantial portions of the Software.
13+
The above copyright notice and this permission notice shall be
14+
included in all copies or substantial portions of the Software.
1415

15-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21-
SOFTWARE.
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

Lines changed: 132 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,138 @@
1-
ALL STILL WIP !!!
1+
[<img src="https://api.travis-ci.org/niesfisch/java-code-tracer.png"/>](http://travis-ci.org/niesfisch/java-code-tracer/builds)
2+
3+
# JCT = Java Code Tracer
4+
5+
JCT was born out of the idea to collect runtime information from a big monolithic (legacy) application that was running for several years. from time to time there was the same question over and over again:
6+
7+
> do we still need this code? nobody seems to be calling it ....
8+
9+
> but i am still scared to remove it :-(
10+
11+
> maybe the code is called in production?
12+
13+
JCT helps you answering these questions by collecting information about your running application.
14+
15+
# Installing JCT for your application
16+
17+
first clone or download this project
18+
19+
## create config
20+
21+
the config file is the place where you configure which classes and packages of your application should be 'monitored' for calls (aka instrumented). by default nothing will be instrumented as this would produce massive amounts of data. start by choosing a sensible package of your application to begin with. everything is based on regex(s), so there should be a lot of freedom for you. see the config-sample.yml that you'll be copying to start with.
22+
23+
```bash
24+
mkdir ${HOME}/.jct/
25+
cp config-sample.yaml ${HOME}/.jct/
26+
```
27+
28+
## built the agent jar that will be used
229

330
```bash
4-
mvn clean install
5-
mkdir ~/.code-tracer/
6-
cp src/test/resources/integration/test-config.yaml ~/.code-tracer
31+
mvn clean package
32+
ls -al ./target/
733
```
834

9-
open `IntegrationTest.java` and run it (see comments in test header)
35+
## start application with agent
36+
37+
(multiline here for better visibility, put everything on one line or escape the linebreaks ;-))
38+
39+
```bash
40+
java -jar someApplication.jar
41+
-javaagent: /path_to/target/java-code-tracer-1.0-SNAPSHOT-jar-with-dependencies.jar
42+
-Djct.loglevel=INFO
43+
-Djct.config=${HOME}/.jct/config-sample.yaml (optional, default is under /src/main/resources/META-INF/config.yaml, will be merged)
44+
-Djct.tracer.server.port=9002 (default=9001)
45+
-Djct.logDir=/tmp/jct
46+
-noverify (needed for the moment)
47+
```
48+
49+
## Server with collected information
50+
51+
when the instrumented app starts a background http server will be started that you can connect to
52+
53+
```bash
54+
curl localhost:[port]
55+
/status/
56+
/stacks/
57+
/purge/
58+
```
59+
60+
## Logging
61+
62+
Logfile can be found under /tmp/jct/jct_agent.log
63+
64+
# Debugging
65+
66+
if you need more information about what will be instrumented etc. tune the loglevel
67+
68+
```bash
69+
-Djct.loglevel=DEBUG
70+
```
71+
72+
and check the logs ...
73+
74+
# How it works
75+
76+
JCT uses byte code instrumentation. it "magically" weaves tracing information into each instrumented class/method to record calls. it keeps a map of call stacks that it collected and their counts. this is done as long as the instrumented process is running and kept in memory. as soon as the process shuts down, the collected information is gone.
77+
78+
## Example Hello World walkthrough
79+
80+
In this walkthrough you will start a simple loop that prints "Hello World ..." to the console. the program will be instrumtented with JCT and some data will be collected. here is a screenshot of the program.
81+
82+
1. config you will use
83+
2. simple loop that calls some methods on other classes
84+
3. sample class calling anohter
85+
4. sample class calling anohter
86+
5. server with status api
87+
6. server with stack api
88+
89+
hint: we explicitly excluded the "HelloWorldLoop" in the config otherwise we would never get results as the loop never leaves and JCT never reaches the end of the stack.
90+
91+
files we will use for demo purposes
92+
93+
[config-sample.yaml](doc/config-sample.yaml)
94+
95+
[helloworld-loop.jar](doc/helloworld-loop.jar)
96+
97+
![Hello World Sample](doc/helloworldloop_overview.png)
98+
99+
now it's time to start it locally ...
100+
101+
```bash
102+
# produce jar with agent
103+
mvn clean package
104+
# start hello world jar with agent attached to it
105+
java -Djct.loglevel=INFO -Djct.config=./doc/config-sample.yaml -Djct.tracer.server.port=9001 -Djct.logDir=/tmp/jct2 -noverify -javaagent:"${PWD}/target/java-code-tracer-1.0-SNAPSHOT-jar-with-dependencies.jar" -jar "${PWD}/doc/helloworld-loop.jar"
106+
```
107+
108+
you should see "Hello World .." printed to the console multiple times.
109+
110+
open another tab ...
111+
112+
... check the logs
113+
114+
```bash
115+
cat /tmp/jct2/jct_agent.log
116+
```
117+
118+
... and check if you can query the server
119+
120+
```bash
121+
curl http://localhost:9001/status/
122+
curl http://localhost:9001/stacks/
123+
curl http://localhost:9001/purge/
124+
```
125+
126+
# ToDos, Ideas
127+
128+
- do not keep in memory, dump somewhere
129+
- stream events somewhere for processing
130+
- create proper frontend for collected traces
131+
- check/improve performance implications
132+
- check/improve threading issues
133+
- max/minpaths count (prevent heap overflow)
134+
- collect largest min/max-n paths
10135

11-
target application needs to be started with `-noverify` (for the moment)
136+
# License
12137

13-
nothing more here for the moment ....
138+
[MIT](LICENSE.txt)

config-sample.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# some comment
2+
classes:
3+
included:
4+
# include one explicit class
5+
- ^some.package.with.ClassA
6+
# include some classes in certain packages that match regex
7+
- ^some.package.with.more.classes.*
8+
# exlude nothing, as per default nothing will be included, use this to tune to wide includes ...
9+
excluded: []

doc/config-sample.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# some comment
2+
classes:
3+
included:
4+
- ^.*hell.*
5+
excluded:
6+
- .*HelloWorldLoop.*

doc/helloworld-loop.jar

4.99 KB
Binary file not shown.

doc/helloworldloop_overview.png

666 KB
Loading

pom.xml

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
<modelVersion>4.0.0</modelVersion>
44

55
<groupId>de.marcel</groupId>
6-
<artifactId>javaagent-sample</artifactId>
6+
<artifactId>java-code-tracer</artifactId>
77
<version>1.0-SNAPSHOT</version>
88
<packaging>jar</packaging>
99

10-
<name>javaagent-sample</name>
10+
<name>java-code-tracer</name>
1111

1212
<properties>
1313
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -25,6 +25,7 @@
2525
<url>see LICENCE.txt</url>
2626
</license>
2727
</licenses>
28+
2829
<repositories>
2930
<repository>
3031
<id>Sonatype-public</id>
@@ -34,6 +35,13 @@
3435
</repositories>
3536

3637
<dependencies>
38+
<dependency>
39+
<groupId>com.sun</groupId>
40+
<artifactId>tools</artifactId>
41+
<version>1.6.0</version>
42+
<scope>system</scope>
43+
<systemPath>${java.home}/../lib/tools.jar</systemPath>
44+
</dependency>
3745
<dependency>
3846
<groupId>org.yaml</groupId>
3947
<artifactId>snakeyaml</artifactId>
@@ -58,29 +66,12 @@
5866
<version>1.9.5</version>
5967
<scope>test</scope>
6068
</dependency>
61-
<dependency>
62-
<groupId>org.powermock</groupId>
63-
<artifactId>powermock-module-junit4</artifactId>
64-
<version>1.5</version>
65-
<scope>test</scope>
66-
</dependency>
67-
<dependency>
68-
<groupId>org.powermock</groupId>
69-
<artifactId>powermock-api-mockito</artifactId>
70-
<version>1.5</version>
71-
<scope>test</scope>
72-
</dependency>
7369
<dependency>
7470
<groupId>log4j</groupId>
7571
<artifactId>log4j</artifactId>
7672
<version>1.2.17</version>
7773
<type>jar</type>
7874
</dependency>
79-
<dependency>
80-
<groupId>biz.minaret.log4j</groupId>
81-
<artifactId>datedFileAppender</artifactId>
82-
<version>1.0.2</version>
83-
</dependency>
8475
</dependencies>
8576
<build>
8677
<plugins>
@@ -110,6 +101,7 @@
110101
<archive>
111102
<manifestEntries>
112103
<Premain-Class>de.marcelsauer.profiler.agent.Agent</Premain-Class>
104+
<Agent-Class>de.marcelsauer.profiler.agent.Agent</Agent-Class>
113105
<Can-Redefine-Classes>true</Can-Redefine-Classes>
114106
<Can-Retransform-Classes>true</Can-Retransform-Classes>
115107
<Can-Set-Native-Method-Prefix>true</Can-Set-Native-Method-Prefix>
@@ -122,9 +114,7 @@
122114
<executions>
123115
<execution>
124116
<id>make-assembly</id>
125-
<!-- this is used for inheritance merges -->
126117
<phase>package</phase>
127-
<!-- bind to the packaging phase -->
128118
<goals>
129119
<goal>single</goal>
130120
</goals>

src/main/java/de/marcelsauer/profiler/agent/Agent.java

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,48 @@
11
package de.marcelsauer.profiler.agent;
22

3+
import java.lang.instrument.Instrumentation;
4+
5+
import org.apache.log4j.Logger;
6+
37
import de.marcelsauer.profiler.config.Config;
48
import de.marcelsauer.profiler.server.Server;
59
import de.marcelsauer.profiler.transformer.Transformer;
6-
import org.apache.log4j.Logger;
7-
8-
import java.lang.instrument.Instrumentation;
910

1011
/**
12+
* start with
13+
* <p/>
14+
* -javaagent: /path-to-jar/code-tracer/target/javaagent-sample-1.0-SNAPSHOT-jar-with-dependencies.jar
15+
* -Dconfig=test-config.yaml (optional)
16+
* -Dtracer.server.port=9002 (default=9001)
17+
* *
18+
* https://web.archive.org/web/20141014195801/http://dhruba.name/2010/02/07/creation-dynamic-loading-and-instrumentation-with-javaagents/
19+
*
1120
* @author msauer
1221
*/
1322
public class Agent {
1423

1524
private static final Logger logger = Logger.getLogger(Agent.class);
1625
private static final String DEFAULT_CONFIG_FILE = "META-INF/config.yaml";
1726

18-
public static void premain(String agentArgs, Instrumentation inst) {
19-
logger.info("registering instrumentation transformer");
27+
public static void agentmain(String args, Instrumentation inst) throws Exception {
28+
init(inst);
29+
}
2030

31+
public static void premain(String agentArgs, Instrumentation inst) {
32+
init(inst);
33+
}
2134

22-
String configFile = DEFAULT_CONFIG_FILE;
23-
Config config = Config.createDefaultFromYamlFile(configFile);
35+
private static void init(Instrumentation inst) {
36+
logger.info("registering instrumentation transformer");
37+
inst.addTransformer(new Transformer(buildConfig()));
38+
new Server().start();
39+
}
2440

41+
private static Config buildConfig() {
42+
Config config = Config.createDefaultFromYamlFile(DEFAULT_CONFIG_FILE);
2543
logger.info("using default config: " + config);
2644

27-
String yamlFile = System.getProperty("config");
45+
String yamlFile = System.getProperty("jct.config");
2846
if (yamlFile != null && !"".equals(yamlFile.trim())) {
2947
Config customConf = Config.createCustomFromYamlFile(yamlFile);
3048
if (customConf.isInclusionConfigured()) {
@@ -34,9 +52,7 @@ public static void premain(String agentArgs, Instrumentation inst) {
3452
config.merge(customConf);
3553
}
3654
logger.info("merged config config" + config);
37-
38-
inst.addTransformer(new Transformer(config));
39-
new Server().start();
55+
return config;
4056
}
4157

4258
private static void ignoreDefaultInclusion(Config config) {

src/main/java/de/marcelsauer/profiler/collect/Collector.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package de.marcelsauer.profiler.collect;
22

3-
import org.apache.log4j.Logger;
4-
53
import java.util.Collections;
64
import java.util.HashMap;
75
import java.util.Map;
86

7+
import org.apache.log4j.Logger;
8+
99
/**
1010
* @author msauer
1111
*/
@@ -15,7 +15,7 @@ public class Collector {
1515

1616
private static final Map<String, Integer> stacks = new HashMap<String, Integer>();
1717

18-
public static void collect(String trace) {
18+
public static synchronized void collect(String trace) {
1919
if (stacks.containsKey(trace)) {
2020
stacks.put(trace, stacks.get(trace) + 1);
2121
} else {

0 commit comments

Comments
 (0)