1 /*
2  * Created on Nov 19, 2003
3  * Created by Alon Rohter
4  *
5  * Copyright (C) Azureus Software, Inc, All Rights Reserved.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  */
19 package org.gudy.azureus2.core3.util;
20 
21 import java.io.*;
22 import java.net.UnknownHostException;
23 import java.net.ConnectException;
24 import java.util.*;
25 
26 /**
27  * Debug-assisting class.
28  */
29 public class Debug {
30 
31 	private static boolean STOP_AT_INITIALIZER = System.getProperty("debug.stacktrace.full", "0").equals("0");
32 
33 	private static AEDiagnosticsLogger	diag_logger;
34 
35 	static{
36 		try{
37 			diag_logger = AEDiagnostics.getLogger( "debug" );
38 
39 			diag_logger.setForced( true );
40 
41 		}catch( Throwable e ){
42 
43 		}
44 	}
45 
46 
47   /**
48    * Prints out the given debug message to System.out,
49    * prefixed by the calling class name, method and
50    * line number.
51    */
out(final String _debug_message)52   public static void out(final String _debug_message) {
53     out( _debug_message, null );
54   }
55 
56   /**
57    * Prints out the given exception stacktrace to System.out,
58    * prefixed by the calling class name, method and
59    * line number.
60    */
out(final Throwable _exception)61   public static void out(final Throwable _exception) {
62     out( "", _exception );
63   }
64 
65   public static void
outNoStack( String str )66   outNoStack(
67   	String		str )
68   {
69   	outNoStack( str, false );
70   }
71 
72   public static void
outNoStack( String str, boolean stderr)73   outNoStack(
74   	String		str,
75 	boolean		stderr)
76   {
77     diagLoggerLogAndOut("DEBUG::"+ new Date(SystemTime.getCurrentTime()).toString() + "  " + str, stderr );
78   }
79 
80   public static void
outDiagLoggerOnly( String str)81   outDiagLoggerOnly(
82   	String		str)
83   {
84     diagLoggerLog(str);
85   }
86 
87   /**
88    * Prints out the given debug message to System.out,
89    * prefixed by the calling class name, method and
90    * line number, appending the stacktrace of the given exception.
91    */
out(final String _debug_msg, final Throwable _exception)92   public static void out(final String _debug_msg, final Throwable _exception) {
93   	if ((_exception instanceof ConnectException) && _exception.getMessage().startsWith("No route to host")) {
94   		diagLoggerLog(_exception.toString());
95   		return;
96   	}
97   	if ((_exception instanceof UnknownHostException)) {
98   		diagLoggerLog(_exception.toString());
99   		return;
100   	}
101     String header = "DEBUG::";
102     header = header + new Date(SystemTime.getCurrentTime()).toString() + "::";
103     String className;
104     String methodName;
105     int lineNumber;
106     String	trace_trace_tail = null;
107 
108     try {
109       throw new Exception();
110     }
111     catch (Exception e) {
112     	StackTraceElement[]	st = e.getStackTrace();
113 
114       StackTraceElement first_line = st[2];
115       className = first_line.getClassName() + "::";
116       methodName = first_line.getMethodName() + "::";
117       lineNumber = first_line.getLineNumber();
118 
119     	trace_trace_tail = getCompressedStackTrace(e, 3, 200, false);
120     }
121 
122     diagLoggerLogAndOut(header+className+(methodName)+lineNumber+":", true);
123     if (_debug_msg.length() > 0) {
124     	diagLoggerLogAndOut("  " + _debug_msg, true);
125     }
126     if ( trace_trace_tail != null ){
127     	diagLoggerLogAndOut( "    " + trace_trace_tail, true);
128     }
129     if (_exception != null) {
130     	diagLoggerLogAndOut(_exception);
131     }
132   }
133 
getLastCaller()134   public static String getLastCaller() {
135   	return getLastCaller(0);
136   }
137 
getLastCaller(int numToGoBackFurther)138   public static String getLastCaller(int numToGoBackFurther) {
139     try {
140       throw new Exception();
141     }
142     catch (Exception e) {
143       // [0] = our throw
144       // [1] = the line that called getLastCaller
145       // [2] = the line that called the function that has getLastCaller
146       StackTraceElement st[] = e.getStackTrace();
147       if (st == null || st.length == 0)
148       	return "??";
149       if (st.length > 3 + numToGoBackFurther)
150         return st[3 + numToGoBackFurther].toString();
151 
152       return st[st.length - 1].toString();
153     }
154   }
155 
getLastCallerShort()156   public static String getLastCallerShort() {
157 	  	return getLastCallerShort(0);
158 	  }
159 
getLastCallerShort(int numToGoBackFurther)160 	  public static String getLastCallerShort(int numToGoBackFurther) {
161 	    try {
162 	      throw new Exception();
163 	    }
164 	    catch (Exception e) {
165 	      // [0] = our throw
166 	      // [1] = the line that called getLastCaller
167 	      // [2] = the line that called the function that has getLastCaller
168 	      StackTraceElement st[] = e.getStackTrace();
169 	      StackTraceElement ste;
170 	      if (st == null || st.length == 0)
171 	      	return "??";
172 	      if (st.length > 3 + numToGoBackFurther){
173 	        ste = st[3 + numToGoBackFurther];
174 	      }else{
175 	    	  ste = st[st.length - 1];
176 	      }
177 	      String fn = ste.getFileName();
178 
179 	      if ( fn != null ){
180 	    	  return( fn + ":" + ste.getLineNumber());
181 	      }
182 	      return( ste.toString());
183 	    }
184 	  }
185 
outStackTrace()186   public static void outStackTrace() {
187     // skip the last, since they'll most likely be main
188 	  diagLoggerLogAndOut(getStackTrace(1),false);
189   }
190 
getStackTrace(int endNumToSkip)191   private static String getStackTrace(int endNumToSkip) {
192 		String sStackTrace = "";
193     try {
194       throw new Exception();
195     }
196     catch (Exception e) {
197       StackTraceElement st[] = e.getStackTrace();
198       for (int i = 1; i < st.length - endNumToSkip; i++) {
199         if (!st[i].getMethodName().endsWith("StackTrace"))
200         	sStackTrace += st[i].toString() + "\n";
201       }
202       if (e.getCause() != null)
203       	sStackTrace += "\tCaused By: " + getStackTrace(e.getCause()) + "\n";
204     }
205     return sStackTrace;
206   }
207 
208 	public static void
killAWTThreads()209 	killAWTThreads()
210 	{
211 		ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
212 
213 		killAWTThreads( threadGroup );
214 	}
215 
216 	private static String
getCompressedStackTrace( Throwable t, int frames_to_skip)217 	getCompressedStackTrace(
218 		Throwable t,
219 		int frames_to_skip)
220 	{
221 		return getCompressedStackTrace(t, frames_to_skip, 200);
222 	}
223 
224 
225 	public static String
getCompressedStackTrace( Throwable t, int frames_to_skip, int iMaxLines)226 	getCompressedStackTrace(
227 		Throwable t,
228 		int frames_to_skip,
229 		int iMaxLines)
230 	{
231 		return getCompressedStackTrace(t, frames_to_skip, iMaxLines, true);
232 	}
233 
234 
235 	public static String
getCompressedStackTrace( Throwable t, int frames_to_skip, int iMaxLines, boolean showErrString)236 	getCompressedStackTrace(
237 		Throwable t,
238 		int frames_to_skip,
239 		int iMaxLines,
240 		boolean showErrString)
241 	{
242 		StringBuffer sbStackTrace = new StringBuffer(showErrString ? (t.toString() + "; ") : "");
243 		StackTraceElement[]	st = t.getStackTrace();
244 
245 		if (iMaxLines < 0) {
246 			iMaxLines = st.length + iMaxLines;
247 			if (iMaxLines < 0) {
248 				iMaxLines = 1;
249 			}
250 		}
251 		int iMax = Math.min(st.length, iMaxLines + frames_to_skip);
252 		for (int i = frames_to_skip; i < iMax; i++) {
253 
254 			if (i > frames_to_skip) {
255 				sbStackTrace.append(", ");
256 			}
257 
258 			String classname = st[i].getClassName();
259 			String cnShort = classname.substring( classname.lastIndexOf(".")+1);
260 
261 			if (Constants.IS_CVS_VERSION) {
262 				if (STOP_AT_INITIALIZER
263 						&& st[i].getClassName().equals(
264 								"com.aelitis.azureus.ui.swt.Initializer")) {
265 					sbStackTrace.append("Initializer");
266 					break;
267 				}
268 				// Formatted so it's clickable in eclipse
269 				// sbStackTrace.append(st[i].toString());
270 
271 				// Shorter version  method(filename.java:1234)
272 				sbStackTrace.append(st[i].getMethodName());
273 				// the space is needed otherwise Eclipse tries to look up method name
274 				sbStackTrace.append(" (");
275 				sbStackTrace.append(st[i].getFileName());
276 				sbStackTrace.append(':');
277 				sbStackTrace.append(st[i].getLineNumber());
278 				sbStackTrace.append(')');
279 			} else {
280 				sbStackTrace.append(cnShort);
281 				sbStackTrace.append("::");
282 				sbStackTrace.append(st[i].getMethodName());
283 				sbStackTrace.append("::");
284 				sbStackTrace.append(st[i].getLineNumber());
285 			}
286 		}
287 
288 		Throwable cause = t.getCause();
289 
290 		if (cause != null) {
291 			sbStackTrace.append("\n\tCaused By: ");
292 			sbStackTrace.append(getCompressedStackTrace(cause, 0));
293 		}
294 
295 		return sbStackTrace.toString();
296 	}
297 
getStackTrace(boolean bCompressed, boolean bIncludeSelf)298 	public static String getStackTrace(boolean bCompressed, boolean bIncludeSelf) {
299 		return getStackTrace(bCompressed, bIncludeSelf, bIncludeSelf ? 0 : 1, 200);
300 	}
301 
getStackTrace(boolean bCompressed, boolean bIncludeSelf, int iNumLinesToSkip, int iMaxLines)302 	public static String getStackTrace(boolean bCompressed, boolean bIncludeSelf,
303 			int iNumLinesToSkip, int iMaxLines) {
304 		if (bCompressed)
305 			return getCompressedStackTrace(bIncludeSelf ? 2 + iNumLinesToSkip
306 					: 3 + iNumLinesToSkip, iMaxLines);
307 
308 		// bIncludeSelf not supported gor non Compressed yet
309 		return getStackTrace(1);
310 	}
311 
getCompressedStackTrace(int frames_to_skip, int iMaxLines)312 	private static String getCompressedStackTrace(int frames_to_skip,
313 			int iMaxLines) {
314 		String trace_trace_tail = null;
315 
316 		try {
317 			throw new Exception();
318 		} catch (Exception e) {
319 			trace_trace_tail = getCompressedStackTrace(e, frames_to_skip, iMaxLines, false);
320 		}
321 
322 		return (trace_trace_tail);
323 	}
324 
325 	public static void
killAWTThreads( ThreadGroup threadGroup )326 	killAWTThreads(
327 		   ThreadGroup	threadGroup )
328 	{
329 		 Thread[] threadList = new Thread[threadGroup.activeCount()];
330 
331 		 threadGroup.enumerate(threadList);
332 
333 		 for (int i = 0;	i < threadList.length;	i++){
334 
335 		 	Thread t = 	threadList[i];
336 
337 		 	if ( t != null ){
338 
339 		 		String 	name = t.getName();
340 
341 		 		if ( name.startsWith( "AWT" )){
342 
343 		 			out( "Interrupting thread '".concat(t.toString()).concat("'" ));
344 
345 		 			t.interrupt();
346 		 		}
347 			}
348 		}
349 
350 		if ( threadGroup.getParent() != null ){
351 
352 			killAWTThreads(threadGroup.getParent());
353 		}
354 	}
355 
356 	public static void
dumpThreads( String name )357 	dumpThreads(
358 		String	name )
359 	{
360 		out(name+":");
361 
362 	  	ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
363 
364 	  	dumpThreads( threadGroup, "\t" );
365 	}
366 
367    public static void
dumpThreads( ThreadGroup threadGroup, String indent )368    dumpThreads(
369    		ThreadGroup	threadGroup,
370    		String		indent )
371    {
372 	  Thread[] threadList = new Thread[threadGroup.activeCount()];
373 
374 	  threadGroup.enumerate(threadList);
375 
376 	  for (int i = 0;	i < threadList.length;	i++){
377 
378 		Thread t = 	threadList[i];
379 
380 		if ( t != null ){
381 
382 		   out( indent.concat("active thread = ").concat(t.toString()).concat(", daemon = ").concat(String.valueOf(t.isDaemon())));
383 		}
384 	  }
385 
386 	  if ( threadGroup.getParent() != null ){
387 
388 	  	dumpThreads(threadGroup.getParent(),indent+"\t");
389 	  }
390    }
391 
392    public static void
dumpThreadsLoop( final String name )393    dumpThreadsLoop(
394    	final String	name )
395    {
396    	new AEThread("Thread Dumper")
397 	   {
398 		   public void
399 		   runSupport()
400 		   {
401 			   while(true){
402 				   Debug.dumpThreads(name);
403 
404 				   try{
405 				   	Thread.sleep(5000);
406 				   }catch( Throwable e ){
407 				   	Debug.printStackTrace( e );
408 				   }
409 			   }
410 		   }
411 	   }.start();
412    }
413 
414 	public static void
dumpSystemProperties()415 	dumpSystemProperties()
416 	{
417 		out( "System Properties:");
418 
419  		Properties props = System.getProperties();
420 
421  		Iterator it = props.keySet().iterator();
422 
423  		while(it.hasNext()){
424 
425  			String	name = (String)it.next();
426 
427  			out( "\t".concat(name).concat(" = '").concat(props.get(name).toString()).concat("'" ));
428  		}
429 	}
430 
431 	public static String
getNestedExceptionMessage( Throwable e )432 	getNestedExceptionMessage(
433 		Throwable 		e )
434 	{
435 		String	last_message	= "";
436 
437 		while( e != null ){
438 
439 			String	this_message;
440 
441 			if ( e instanceof UnknownHostException ){
442 
443 				this_message = "Unknown host " + e.getMessage();
444 
445 			}else if ( e instanceof FileNotFoundException ){
446 
447 				this_message = "File not found: " + e.getMessage();
448 
449 			}else{
450 
451 				this_message = e.getMessage();
452 			}
453 
454 				// if no exception message then pick up class name. if we have a deliberate
455 				// zero length string then we assume that the exception can be ignored for
456 				// logging purposes as it is just delegating
457 
458 			if ( this_message == null ){
459 
460 				this_message = e.getClass().getName();
461 
462 				int	pos = this_message.lastIndexOf(".");
463 
464 				this_message = this_message.substring( pos+1 ).trim();
465 			}
466 
467 			if ( this_message.length() > 0 && last_message.indexOf( this_message ) == -1 ){
468 
469 				last_message	+= (last_message.length()==0?"":", " ) + this_message;
470 			}
471 
472 			e	= e.getCause();
473 		}
474 
475 		return( last_message );
476 	}
477 
478 	public static boolean
containsException( Throwable error, Class<? extends Throwable> cla )479 	containsException(
480 		Throwable					error,
481 		Class<? extends Throwable>	cla )
482 	{
483 		if ( error == null ){
484 
485 			return( false );
486 
487 		}else if ( cla.isInstance( error )){
488 
489 			return( true );
490 		}
491 
492 		return( containsException( error.getCause(), cla ));
493 	}
494 
495 	public static String
getNestedExceptionMessageAndStack( Throwable e )496 	getNestedExceptionMessageAndStack(
497 		Throwable 		e )
498 	{
499 		return( getNestedExceptionMessage(e) + ", " + getCompressedStackTrace( e, 0 ));
500 	}
501 
502 	public static String
getCompressedStackTraceSkipFrames( int frames_to_skip )503 	getCompressedStackTraceSkipFrames(
504 		int	frames_to_skip )
505 	{
506 		return( getCompressedStackTrace( new Throwable(), frames_to_skip+1, 200, false ));
507 	}
508 
509 	public static String
getCompressedStackTrace()510 	getCompressedStackTrace()
511 	{
512 		return( getCompressedStackTrace( new Throwable(), 1, 200, false ));
513 	}
514 
515 	/**
516 	 *
517 	 * @param iMaxLines Max # of stack lines.  If < 0, chops off -MaxLines entries from end
518 	 * @return
519 	 */
520 	public static String
getCompressedStackTrace(int iMaxLines)521 	getCompressedStackTrace(int iMaxLines)
522 	{
523 		return( getCompressedStackTrace( new Throwable(), 1, iMaxLines, false ));
524 	}
525 
526 	public static String
getExceptionMessage( Throwable e )527 	getExceptionMessage(
528 		Throwable	e )
529 	{
530 		String	message = e.getMessage();
531 
532 		if ( message == null || message.length() == 0 ){
533 
534 			message = e.getClass().getName();
535 
536 			int	pos = message.lastIndexOf(".");
537 
538 			message = message.substring( pos+1 );
539 
540 		}else if ( e instanceof ClassNotFoundException ){
541 
542 			if ( message.toLowerCase().indexOf("found") == -1 ){
543 
544 				message = "Class " + message + " not found";
545 			}
546 		}
547 
548 		return( message );
549 	}
550 
printStackTrace(Throwable e)551 	public static void printStackTrace(Throwable e) {
552 		printStackTrace(e, null);
553 	}
554 
555 
556 	public static void
printStackTrace( Throwable e, Object context)557 	printStackTrace(
558 		Throwable e,
559 		Object context)
560 	{
561   	if ((e instanceof ConnectException) && e.getMessage().startsWith("No route to host")) {
562   		diagLoggerLog(e.toString());
563   		return;
564   	}
565   	if ((e instanceof UnknownHostException)) {
566   		diagLoggerLog(e.toString());
567   		return;
568   	}
569 		String header = "DEBUG::";
570 		header = header + new Date(SystemTime.getCurrentTime()).toString() + "::";
571 		String className	= "?::";
572 		String methodName	= "?::";
573 		int lineNumber		= -1;
574 
575 	    try {
576 	        throw new Exception();
577 	    }catch (Exception f) {
578 	      	StackTraceElement[]	st = f.getStackTrace();
579 
580 	      	for (int i=1;i<st.length;i++){
581 		        StackTraceElement first_line = st[i];
582 		        className = first_line.getClassName() + "::";
583 		        methodName = first_line.getMethodName() + "::";
584 		        lineNumber = first_line.getLineNumber();
585 
586 		        	// skip stuff generated by the logger
587 
588 		        if ( 	className.indexOf( ".logging." ) != -1 ||
589 		        		className.endsWith( ".Debug::" )){
590 
591 		        	continue;
592 		        }
593 
594 		        break;
595 	      }
596 	    }
597 
598 	    diagLoggerLogAndOut(header+className+(methodName)+lineNumber+":", true);
599 
600 		try{
601 			ByteArrayOutputStream	baos = new ByteArrayOutputStream();
602 
603 			PrintWriter	pw = new PrintWriter( new OutputStreamWriter( baos ));
604 
605 			if (context!=null) {pw.print("  "); pw.println(context);}
606 			pw.print("  ");
607 			e.printStackTrace( pw );
608 
609 			pw.close();
610 
611 			String	stack = baos.toString();
612 
613 			diagLoggerLogAndOut(stack, true );
614 		}catch( Throwable ignore ){
615 
616 			e.printStackTrace();
617 		}
618 	}
619 
getStackTrace(Throwable e)620 	public static String getStackTrace(Throwable e) {
621 		try {
622 			ByteArrayOutputStream baos = new ByteArrayOutputStream();
623 
624 			PrintWriter pw = new PrintWriter(new OutputStreamWriter(baos));
625 
626 			e.printStackTrace(pw);
627 
628 			pw.close();
629 
630 			return baos.toString();
631 
632 		} catch (Throwable ignore) {
633 			return "";
634 		}
635 	}
636 
diagLoggerLog(String str)637 	private static void diagLoggerLog(String str) {
638 		if ( diag_logger == null ){
639 			System.out.println( str );
640 		}else{
641 			diag_logger.log(str);
642 		}
643 	}
644 
645 	private static void
diagLoggerLogAndOut( String str, boolean stderr )646 	diagLoggerLogAndOut(
647 		String	str,
648 		boolean	stderr )
649 	{
650 			// handle possible recursive initialisation problems where the init of diag-logger gets
651 			// back here....
652 
653 		if ( diag_logger == null ){
654 			if ( stderr ){
655 				System.err.println( str );
656 			}else{
657 				System.out.println( str );
658 			}
659 		}else{
660 			diag_logger.logAndOut( str, stderr );
661 		}
662 	}
663 	private static void
diagLoggerLogAndOut( Throwable e )664 	diagLoggerLogAndOut(
665 		Throwable e )
666 	{
667 			// handle possible recursive initialisation problems where the init of diag-logger gets
668 			// back here....
669 
670 		if ( diag_logger == null ){
671 			e.printStackTrace();
672 		}else{
673 			diag_logger.logAndOut( e );
674 		}
675 	}
676 
677 	/**
678 	 * @param key
679 	 * @return
680 	 */
secretFileName(String key)681 	public static String secretFileName(String key) {
682 		if (key == null)
683 			return "";
684 
685 		final String sep = File.separator;
686 		final String regex = "([\\" + sep + "]?[^\\" + sep + "]{0,3}+)[^\\" + sep
687 				+ "]*";
688 
689 		String secretName = key.replaceAll(regex, "$1");
690 		int iExtensionPos = key.lastIndexOf(".");
691 		if (iExtensionPos >= 0)
692 			secretName += key.substring(iExtensionPos);
693 		return secretName;
694 	}
695 
main(String[] args)696 	public static void main(String[] args) {
697 		System.out.println(secretFileName("c:\\temp\\hello there.txt"));
698 		System.out.println(secretFileName("hello there.txt"));
699 	}
700 }
701