package cis75; import java.util.Hashtable; import java.util.Stack; import java.util.Vector; import java.io.*; /** * <p>Title: CallTrace</p> * <p>Description: internal CIS75 tracing facility</p> * <p>Copyright: Copyright (c) 2008</p> * <p>Company: Bristol Community College</p> * @author Igor Kholodov, CIS Instructor * @version 1.0 */ public class CallTrace { public static String strDebugOutput = "debugout.txt"; private static boolean bTraceActive = true; private static String strActivateTrace = ""; private static int nPreviousTraceLevel = 999; private static String strPreviousLocation = ""; private static Hashtable htStats = new Hashtable(); // maps function name to function stats private static Hashtable htFunctionMap = new Hashtable(); // maps function name to fid private static Vector vFids = new Vector(); // reverse map from fid to function name private static Hashtable htCallHistory = new Hashtable(); // keeps track of calls received private static Stack CallStack = new Stack(); // call stack private static int fid_generator = 0; // each function gets unique id private static String strTraceLevel = "<-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=?"; //private static String strTraceLevel = "...........................................................................................................................?"; public static final void trace2sql(int nEvent, String strLocation) { /* Trace engine usage: CallTrace.trace2sql( 4, "Module.Function()" ); // IN CallTrace.trace2sql( 0, "Module.Function()" ); // OUT CallTrace.trace2sql( 1, "Module.Function()" ); // IN/OUT e.g. stored procedure CallTrace.trace2sql( 2, "any text" ); // Console device output CallTrace.trace2sql( 3, "any text" ); // Error device output // Dump stats, etc. CallTrace.trace2sql( 5, "Module.Function()" ); // Dump stats CallTrace.trace2sql( 6, "Module.Function()" ); // RESET TRACE - deletes trace history info CallTrace.trace2sql( 7, "Module.Function()" ); // Dump fids // Activate/deactivate trace: CallTrace.trace2sql( 8, "your text" ); // DEACTIVATE TRACE: tracing stops; activated by next "your text" output. CallTrace.trace2sql( N, "your text" ); // ACTIVATE TRACE: trace resumes, stack size increased by N. // Dump stack CallTrace.trace2sql( 9, "Module.Function()" ); // dump current call stack Example: the following stack dump is produced by trace static final void readDealList() { CallTrace.trace2sql( 4, "RunScript.readDealList()" ); CallTrace.trace2sql( 9, "RunScript.readDealList()" ); //DUMP STACK ... } <-=>RunScript.readDealList() <-=->RunScript.readDealList() *** STACK DUMP *** [1, 205, 206, 207] <-=- 207 RunScript.readDealList() <-=- 206 RunScript.checkDealList() <-=- 205 RunScript.getDeal() <-=- 1 RunScript.main() <-=- *** STACK BOTTOM *** */ int fid = 0; // function id int previous_fid = 0; // previous function id at a given level int pid = 0; // parent id int nTraceLevel = CallStack.size(); // current trace level // Special events: if (!bTraceActive) { if (strLocation.equals(strActivateTrace)) { // ACTIVATE TRACE // activate trace by output trace2file(nTraceLevel = nEvent, " *** ACTIVATE TRACE ***"); bTraceActive = true; while (nTraceLevel-- > 0) CallStack.push(new Integer(0)); } return; } if (nEvent == 5) { // dump stats trace2file(nTraceLevel, strLocation + " DUMP STATS" + htStats.toString()); return; } if (nEvent == 6) { // reset trace trace2file(nTraceLevel, strLocation + " *** RESET TRACE ***"); htCallHistory.clear(); return; } if (nEvent == 7) { // dump fids trace2file(nTraceLevel, strLocation + " DUMP FIDs" + htFunctionMap.toString()); return; } if (nEvent == 2) // console output System.out.println(strLocation); else if (nEvent == 3) // error output System.err.println(strLocation); if (nEvent == 8) { // deactivate trace strActivateTrace = strLocation; trace2file(nTraceLevel, " *** DEACTIVATE TRACE ***"); bTraceActive = false; return; } if (nEvent == 9) { // dump stack trace2file(nTraceLevel, strLocation + " *** STACK DUMP *** " + CallStack.toString()); for (int iSP = CallStack.size(); iSP >= 0; --iSP) { if (iSP == 0) trace2file(nTraceLevel, "*** STACK BOTTOM ***", " "); else { fid = ((Integer)CallStack.elementAt(iSP - 1)).intValue(); if (fid == 0) trace2file(nTraceLevel, fid + "\t" + "???", " "); else trace2file(nTraceLevel, fid + "\t" + (String)vFids.elementAt(fid - 1), " "); } } return; } // End of special events else { // inout=1, in=4, out=0 // obtain fid of the caller: if (htFunctionMap.containsKey(strLocation)) fid = ((Integer)htFunctionMap.get(strLocation)).intValue(); else { htFunctionMap.put(strLocation, new Integer(fid = ++fid_generator)); vFids.addElement(strLocation); } if (!CallStack.empty()) { pid = ((Integer)CallStack.peek()).intValue(); // get parent function id String strHistoryKey = nTraceLevel + "x" + pid; if (htCallHistory.containsKey(strHistoryKey)) previous_fid = ((Integer)htCallHistory.get(strHistoryKey)).intValue(); // get previous function id htCallHistory.put(strHistoryKey, new Integer(fid)); } } String strHistoryKey = nTraceLevel + "x" + pid + "x" + previous_fid + "x" + fid; boolean bOldItem = htCallHistory.containsKey(strHistoryKey); int call_count = 0; // function call count switch (nEvent) { case 1: if (!bOldItem) { htCallHistory.put(strHistoryKey, new Integer(fid)); trace2file(nTraceLevel, strLocation); } //else trace2file( nTraceLevel, strLocation ); // UNCONDITIONAL TRACING // in/out - Update statistics if (htStats.containsKey(strLocation)) call_count = ((Integer)htStats.get(strLocation)).intValue(); // get previous function id htStats.put(strLocation, new Integer(++call_count)); return; case 2: trace2file(nTraceLevel, strLocation, "|"); return; case 3: trace2file(nTraceLevel, strLocation, " *** "); return; case 0: // out if (!bOldItem) htCallHistory.put(strHistoryKey, new Integer(fid)); while (!CallStack.empty() && (fid != (pid = ((Integer)CallStack.pop()).intValue()))) { // we expect to find this fid on the stack of calls trace2file(nTraceLevel = CallStack.size(), "return; //" + pid); if (pid == 0) // when trace is activated / deactivated, default stack is populated with an arbitrary number of zero-parents. break; } // out - Update statistics if (htStats.containsKey(strLocation)) call_count = ((Integer)htStats.get(strLocation)).intValue(); // get previous function id htStats.put(strLocation, new Integer(++call_count)); return; // out case 4: if (nEvent == 4) CallStack.push(new Integer(fid)); // current function id will now be the parent if (!bOldItem) { htCallHistory.put(strHistoryKey, new Integer(fid)); trace2file(nTraceLevel, strLocation); } //else trace2file( nTraceLevel, strLocation ); // UNCONDITIONAL TRACING } } public static final void trace2file(int nTraceLevel, String strLocation) { trace2file(nTraceLevel, strLocation, ">"); } public static final void trace2file(int nTraceLevel, String strLocation, String strPrefix) { if ((nTraceLevel == nPreviousTraceLevel) && strPreviousLocation.equals(strLocation)) return; nPreviousTraceLevel = nTraceLevel; strPreviousLocation = strLocation; try { FileOutputStream fileOut = new FileOutputStream(strDebugOutput, true); PrintStream strmOut = new PrintStream(fileOut); //strmOut.println( "csx_trace " + nEvent + ", " + strLocation ); //strmOut.println( "go" ); if (strTraceLevel.length() > nTraceLevel) strmOut.println(strTraceLevel.substring(0, nTraceLevel) + strPrefix + strLocation); else strmOut.println(strTraceLevel + strLocation); strmOut.close(); fileOut.close(); } catch (java.lang.Exception ex) { System.out.println("CallTrace.trace2file has failed"); System.out.println("--" + strLocation + "--"); ex.printStackTrace(); } } } //CallTrace