How to generate a call graph for a java project - java

Is it possible to generate a global call graph of an application?
Basically I am trying to find the most important class of an application.
I am looking for options for Java.
I have tried Doxy Gen, but it only generates inheritance graphs.
My current script:
#! /bin/bash
echo "digraph G
{"
find $1 -name \*.class |
sed s/\\.class$// |
while read x
do
javap -v $x | grep " = class" | sed "s%.*// *%\"$x\" -> %" | sed "s/$1\///" | sed "s/-> \(.*\)$/-> \"\1\"/"
done
echo "}"

javap -v and a bit of perl will get you dependencies between classes. You can make your parser slightly more sophisticated and get dependencies between methods.
Update: or if you have either a *nix or cygwin you can get a list of dependencies as
find com/akshor/pjt33/image -name \*.class |
sed s/\\.class$// |
while read x
do
javap -v $x | grep " = class" | sed "s%.*// *%$x -> %"
done
Add a header and a footer and you can pass it to dot to render a graph. If you just want to know which classes are used by the most other classes, as your question implies, then
find com/akshor/pjt33/image -name \*.class |
sed s/\\.class$// |
while read x
do
javap -v $x | grep " = class" | sed "s%.*// *%%"
done |
sort | uniq -c | sort -n

For advanced code analysis you might wanna have a look at http://www.moosetechnology.org/
Cheers
Thomas
(edit: moved down here by general request, See: How to generate a Java call graph)

Related

Get count of java classes loaded per library

In the AWS re:invent presentation on Lambda performance (highly recommend) on pp.33-34 the author lists the count of classes loaded within each library using the following command:
java -cp my.jar -verbose:class Handler | grep '\[Loaded' | grep '.jar\]' | sed -r 's/\[Loaded \([^A-Z]*\)[\$A-Za-z0-9]*from.*\]/\1/g' | sort | uniq -c | sort
This basically extracts the namespace up to but not including the first capital letter, which is the class name. The output is supposed to look something like this:
143 com.fasterxml.jackson
219 org.apache.http
373 com.google
507 com.amazonaws
However this only works with the Java 8 class loader logs, which have the following format (this example should output java.io):
[Loaded java.io.Serializable from shared objects file]
The class loader logs as of Java 9+ have this different format:
[0.041s][info][class,load] java.io.Serializable source: jrt:/java.base
How does the sed command need to be updated to produce the same output as above?
I've tried the following, but the entire line is extracted in the regex group, not just the class library. I'm also running on a Mac, so I had to add a -r flag and remove some of the escape characters:
java -cp my.jar -verbose:class Handler | grep '[class,load]' | grep '.jar' | sed -r 's/.*\[class,load\] ([^A-Z]*)[$A-Za-z0-9]*source.*/\1/g'
Since the record has fields space separated we can take advantage of cut to get the desired field and then use sed to extract the package substring. The ([a-z.]+)\.[A-Z].* regex looks for lower case letters and dots until the first dot followed by an upper case letter.
echo "[0.041s][info][class,load] java.io.Serializable source: jrt:/java.base" | cut -d ' ' -f2 | sed -E 's/([a-z.]+)\.[A-Z].*/\1/g'
Result:
java.io
If a sed only solution is preferred this command will do grep and cut jobs as well:
echo "[0.041s]..." | sed -nE '/class,load/ s/[^ ]+ ([^ ]+)/\1/ ; s/([a-z.]+)\.[A-Z].*/\1/p'
grep : /class,load/
cut : s/[^ ]+ ([^ ]+)/\1/
extract: s/([a-z.]+)\.[A-Z].*/\1/p

Running pipelined commands using ProcessBuilder in Java

I am trying to extract the frequency of the most frequent line in a file using the following command:
sort file.txt | uniq -c | sort -r | head -1| xargs
I am trying to accomplish from within a Java program, using the ProcessBuilder class. Here is how I am passing to its constructor:
ProcessBuilder builder=new ProcessBuilder("/bin/sh", "-c","sort",fileName,"| uniq -c | sort -r | head -1 | xargs");
When I run the program, it just stops executing beyond this line. There are no errors, but the program just halts at this line. What is it that I might be doing wrong?
Thanks!
Try including a filename directly into command:
ProcessBuilder builder=new ProcessBuilder("/bin/sh", "-c","sort " + fileName + " | uniq -c | sort -r | head -1 | xargs");

String concatenating error in shell script executing

This is a wired problem confusing me for days.I want to get a class's full class name from parse the java code file in shell.We can get package name from like:
package com.android.mail.ui;
and get class name from code file path,use shell command 'basename'.
below is my shell scripts:
#!/bin/bash
get_package_name(){
java_file=$1
if [ ! -f $file_path ]; then
echo "Sorry,the java file is not exist:$1,please check"
exit 1
fi
class_base_name=`basename "$java_file" .java`
echo "class_base_name:$class_base_name"
package_name=`grep $java_file -e "^package" | awk -F " " '{print $2}' | tr ';' ' ' | sed 's/ //g'`
echo "package_name get result:$?"
echo "package_name:$package_name"
method 1,use variable concat directly
classpath_name=$package_name.$class_base_name
echo "method 1 classpath_name:$classpath_name"
method 2,use sed replace to get concat indirectly
classpath_name2=`echo "aa.bb" | sed "s/aa/$package_name/" | sed "s/bb/$class_base_name/"`
echo "method 2 classpath_name2:$classpath_name2"
}
The problem is:for some code file the result is ok,like:
"class_base_name:MailTransport package_name get result:0
package_name:com.android.email.mail.transport method 1
classpath_name:com.android.email.mail.transport.MailTransport method 2
classpath_name2:com.android.email.mail.transport.MailTransport"
for others it's output is : "class_base_name:EmailApplication
package_name get result:0 package_name:com.android.email
.EmailApplicationh_name:com.android.email
.EmailApplicationh_name2:com.android.email"
the result is totally messing and wrong.I doubt it relates the code
content,that really make sense for the result?
This happens because some of your files use Windows style CRLF (\r\n) line terminators.
Here's an example where it works, a normal Unix style LF (\n) terminated file:
$ file WorkingFile.java
WorkingFile.java: ASCII text
$ cat -v WorkingFile.java
package foo.bar.baz;
$ get_package_name WorkingFile.java
class_base_name:WorkingFile
package_name get result:0
package_name:foo.bar.baz
method 1 classpath_name:foo.bar.baz.WorkingFile
Here's an example where it fails, with CRLF line terminators:
$ file FailingFile.java
FailingFile.java: ASCII text, with CRLF line terminators
$ cat -v FailingFile.java
package foo.bar.baz;^M <--- note hidden control char revealed by -v
$ get_package_name FailingFile.java
class_base_name:FailingFile
package_name get result:0
package_name:foo.bar.baz
.FailingFilesspath_name:foo.bar.baz
To fix it, you can delete the extra carriage returns using tr -d '\r'. I switched from legacy backticks to modern $() to avoid problems with backslashes:
package_name=$(grep $java_file -e "^package" | awk -F " " '{print $2}' | tr ';' ' ' | sed 's/ //g' | tr -d '\r')
For more information, see this relevant post.

How to run Java class without knowing a class name

I have a content of compiled Java class as a binary file (named X.class). Let's assume it has some class inside with a proper main method, but I don't know original class name (and it's not X).
How can I run it with java using single command line?
You can get the name of the class using javap -c <filename>. From that point on, it's just shell games. This works on OS X:
java $(javap -c File.class | head -n 2 | tail -n1 | cut -f3 -d\ )
Note the space after the \ in the subshell - this is requires to isolate the class name.
Update based on #nikolay-vyahhi feedback:
If you're not sure at which position the class name will appear (e.g. when it's unknown whether the class is public or not), you can use tr and grep, like so:
java $(javap -c File.class | head -n2| tail -n1 | tr [:space:] \\n | grep -A1 "class\|interface" | tail -n1)
You can use javap to do that:
javap X.class
It will disassemble the class and print out the package, protected, and public fields and methods:
Compiled from "Test.java"
public class Test {
public Test();
public static void main(java.lang.String...);
}
Then you can use this to run the application:
java $(javap X.class | sed -n 's/.*class \(.*\) {/\1/p')

Grep for the 5 lines after a specific text string found in files

I have many java files, and I want to find how many times we are logging via
logger.isDebugEnabled(){
logger.Debug("some debug message");
}
To get an idea of how often we may be overusing the isDebugEnabled function. I have found the number of times we have called/where it is called via
grep -r "isDebugEnabled" --include=*.java . | wc -l
But I want to know how many of those are 1 line statements. Does anyone have a good script to search for this or any ideas on the most efficient way of doing this?
Don’t use grep for this, use the following AWK program:
prev ~ /isDebugEnabled/ && $0 ~ /logger\.Debug\("[^"]"\)/ {
print FILENAME ":" NR ": " $0
}
{
prev = $0
}
This program remembers the previous line in the prev variable and thereby allows you to compare two lines at a time.
To actually use it, write:
find . -name '*.java' -print \
| xargs awk 'prev ~ /isDebugEnabled/ && /logger\.Debug\("[^"]"\)/ { print FILENAME ":" NR ": " $0 } { prev = $0 }'
As mentioned in comments grep provides options to print certain number of lines after and before match.
To print lines after match:
grep -A 2 "string to match" file.txt
To print lines before match:
grep -B 2 "string to match" file.txt
Before you try to write a script giving one final answer, try different approaches for insight what is best for what you want. Test with one file.
Do you think, that logger.Debug("The database has ", getDbNum(), "and the server has ", getRemoteNumSpecial(), "records."); is a simple oneliner?
You can collect some numbers for a first orientation. The examples beneath is using x.java as example sourcefile.
# Nr of isDebugEnabled calls
grep -c "logger.isDebugEnabled" x.java
# Some may be comment with //
grep -c "//.*logger.isDebugEnabled" x.java
# How much debug-lines
grep -c "logger.Debug" x.java
# How much debug-lines with more than 1 parameter (having a ,)
grep -c "logger.Debug.*," x.java
# How much debug-lines without the closing ) on the same line
grep "logger.Debug" x.java | grep -v "Debug.*)"
# How much logger.isDebugEnabled() without a logger.Debug on the next line
grep -A1 "logger.isDebugEnabled" x.java | grep -c "logger.Debug"
# How much logger.Debug a "}" on the next line
# The debugline might have a }, so skip lines with logger.Debug
grep -A1 "logger.Debug" x.java | grep -v "logger.Debug" | grep -c "}"

Categories