/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jvm.instrument.util;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
/**
* Tracing thread is one of the instrument thread. It aims to get all java threads' status periodically, and print them out.
* Current collected status includes: thread name, id, status, callstacks, isDaemon and etc.
*
*
*/
public class TracingThread extends InstrumentThread {
//private String traceoutput = null; //output folder for @taskid@.log
private String traceoutputfile = null; //traceoutputfile
private int traceinterval = 20; //traceinterval
private int tracedepth = -1; //tracedepth in millisecond
private AgentOutput output = null;
private boolean closed = false;
private String style = "line";
private String taskid = "";
//Not enabled yet
private int duration = -1; //This duration is to control the running time of thie java agent thread
public void setTaskID(String id){
this.taskid = id;
}
public void setDuration(int duration){
this.duration = duration;
}
public void setTraceOutputFile(String traceoutputfile) {
this.traceoutputfile = traceoutputfile;
}
public void setTraceInterval(int traceinterval) {
this.traceinterval = traceinterval;
}
public void setTraceDepth (int tracedepth) {
this.tracedepth = tracedepth;
}
public void setStyle (String style){
this.style = style;
}
public TracingThread(AgentConf conf) {
if (this.Init(conf)) {
output = new FileOutput(this.traceoutputfile);
//output = new AgentOutput(this.traceoutputfile);
output.setBuffersize(Integer.parseInt(conf.getProperty("buffersize")));
if(this.style.equals("line")){
try {
output.write("TimeStamp"+InstrumentThread.COMMA);
output.write("ThreadID" + InstrumentThread.COMMA);
output.write("ThreadName" + InstrumentThread.COMMA );
output.write("isDaemon" + InstrumentThread.COMMA);
output.write("ThreadPriority" + InstrumentThread.COMMA);
output.write("ThreadState" + InstrumentThread.COMMA);
if(!taskid.equals(""))
output.write("TaskID" + InstrumentThread.COMMA);
output.write("CallStack"+InstrumentThread.LINEBREAKE);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
this.Cleanup();
}
}
}
}
public boolean Init(AgentConf conf){
boolean done = true;
this.setDaemon(true);
this.setName("MonitorThreadTrace");
if(conf.getProperty("traceoutput")!=null && !conf.getProperty("traceoutput").equals(""))
this.setTraceOutputFile(conf.getProperty("traceoutput")+"/"+conf.getProperty("taskid")+".log");
this.setTraceInterval(Integer.parseInt(conf.getProperty("traceinterval")));
this.setTraceDepth(Integer.parseInt(conf.getProperty("tracedepth")));
this.setStyle(conf.getProperty("style"));
this.setTaskID(conf.getProperty("taskid"));
return done;
}
/**
* Get the stack trace of all threads except itself
* @param thread
* @param stackInfo
*/
private void PrintStackTrace(Thread thread, StackTraceElement[] stackInfo)throws IOException {
String name = thread.getName(); //Get the thread name
long id = thread.getId(); //Get the thread id
boolean isDaemon = thread.isDaemon(); //Get the thread is daemon or not
int priority = thread.getPriority(); //Get the thread priority
State state = thread.getState(); //Get the thread state
if(this.style.equals("blk")){
output.write("\"" + name + "\"" + InstrumentThread.SPACE );
if(!taskid.equals(""))
output.write("taskid=" + taskid + InstrumentThread.SPACE);
output.write("tid=" + id + InstrumentThread.SPACE);
output.write("daemon=" + isDaemon + InstrumentThread.SPACE);
output.write("priority=" + priority + InstrumentThread.SPACE);
output.write("state=" + state.toString() + InstrumentThread.LINEBREAKE);
}
else if (this.style.equals("line")){
output.write(id + InstrumentThread.COMMA);
output.write("\"" + name + "\"" + InstrumentThread.COMMA );
output.write(isDaemon + InstrumentThread.COMMA);
output.write(priority + InstrumentThread.COMMA);
output.write(state.toString() + InstrumentThread.COMMA);
if(!taskid.equals(""))
output.write(taskid + InstrumentThread.COMMA);
}
if ( stackInfo.length > 0 ) {
//trace for each thread
int depth = stackInfo.length;
if(this.tracedepth > 0){
depth = stackInfo.length < this.tracedepth ? stackInfo.length: this.tracedepth;
}
//int depth = stackInfo.length < this.tracedepth ? stackInfo.length: this.tracedepth;
for ( int i = 0; i < depth; i++) {
String classname = stackInfo[i].getClassName();
String methodname = stackInfo[i].getMethodName();
if(this.style.equals("blk")){
output.write(InstrumentThread.TAB +"at" + InstrumentThread.SPACE );
output.write(classname + InstrumentThread.DOT + methodname + InstrumentThread.SPACE );
}
else if (this.style.equals("line")){
output.write(classname + InstrumentThread.DOT + methodname + InstrumentThread.SPACE );
if (i < (depth -1)){
output.write(InstrumentThread.SEPERATE_MARK);
}
}
if(this.style.equals("blk")){
output.write(InstrumentThread.LINEBREAKE);
}
}
}
output.write(InstrumentThread.LINEBREAKE);
}
/**
* Dump the thread tracing information periodically
*/
public void run() {
try{
while(output!=null && !Thread.interrupted()) {
long timestamp = System.currentTimeMillis();
if(this.style.equals("blk")){
output.write("Timestamp:" + InstrumentThread.SPACE + timestamp + InstrumentThread.LINEBREAKE);
output.write("=== Get All Thread Stack Traces ===" + InstrumentThread.LINEBREAKE);
}
//getAllStackTraces
Map<Thread,StackTraceElement[]> traceinfo = Thread.getAllStackTraces();
if(traceinfo.size() > 0){
//If the map is not empty
Iterator<Entry<Thread, StackTraceElement[]>> iter = traceinfo.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Thread, StackTraceElement[]> entry = (Map.Entry<Thread, StackTraceElement[]>) iter.next() ;
Thread key = (Thread) entry.getKey() ;
if(key!=this){
StackTraceElement[] value = (StackTraceElement[]) entry.getValue();
if (this.style.equals("line")){
output.write(timestamp+InstrumentThread.COMMA);
}
PrintStackTrace(key, value);
}
}
}
if(this.style.equals("blk")){
output.write("=== End of All Thread Stack Traces ===" + InstrumentThread.LINEBREAKE);
output.write(InstrumentThread.LINEBREAKE);
}
try {
if(this.traceinterval > 0 && !Thread.interrupted()){
Thread.sleep(this.traceinterval);
}
else {
break;
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
break;
}
}
}
catch (Exception e) {
// TODO Auto-generated catch block
this.Cleanup();
//e.printStackTrace();
}
}
public boolean isClosed(){
return this.closed;
}
public boolean Cleanup(){
//close Agentoutput
if(closed)return closed;
try{
if(output!=null)output.close();
}
catch(Exception e){
e.printStackTrace();
return closed;
}
output = null;
closed = true;
return closed;
}
}