/**
* Copyright (C) 2002-2012 The FreeCol Team
*
* This file is part of FreeCol.
*
* FreeCol is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* FreeCol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with FreeCol. If not, see <http://www.gnu.org/licenses/>.
*/
package net.sf.freecol.common.debug;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.logging.LogRecord;
import net.sf.freecol.client.FreeColClient;
import net.sf.freecol.server.FreeColServer;
/**
* High-level debug handling.
* Throughout the code, routines check getDebugLevel(). The level enables
* various things:
* - OFF turns on nothing
* - LIMITED turns on lots of miscellaneous stuff
* - debug menu actions including turn skipping
* - extra commands
* - Set Goods in ColonyPanel
* - Take Ownership in TilePopup
* - DebugForeignColony in Canvas
* - extra displays
* - goods-in-market tooltip in MarketLabel
* - display of region and mission in MapViewer
* - verbose (non-i18n) server error message display
* - FULL turns on the prepopulated colony in new games
* - FULL_COMMS turns on trace printing of c-s communications
*/
public class FreeColDebugger {
public static final int DEBUG_OFF = 0;
public static final int DEBUG_LIMITED = 1;
public static final int DEBUG_FULL = 2;
public static final int DEBUG_FULL_COMMS = 3;
private static int debugLevel = DEBUG_OFF;
/**
* The number of turns to run without stopping.
*/
private static int debugRunTurns = -1;
/**
* The name of a file to save to at the end of a debug run.
*/
private static String debugRunSave = null;
/**
* Configures the debug level.
*
* @param optionValue The command line option.
*/
public static void configureDebugLevel(String optionValue) {
try {
setDebugLevel(Math.min(Math.max(Integer.parseInt(optionValue),
DEBUG_OFF),
DEBUG_FULL_COMMS));
} catch (NumberFormatException e) {
setDebugLevel(DEBUG_FULL);
}
}
/**
* Configures a debug run.
*
* @param option The command line option.
*/
public static void configureDebugRun(String option) {
int comma = option.indexOf(",");
String turns = option.substring(0, (comma < 0) ? option.length() : comma);
try {
setDebugRunTurns(Integer.parseInt(turns));
} catch (NumberFormatException e) {
setDebugRunTurns(-1);
}
if (comma > 0) setDebugRunSave(option.substring(comma + 1));
}
/**
* Gets the debug level.
*
* @return The debug level.
*/
public static int getDebugLevel() {
return FreeColDebugger.debugLevel;
}
/**
* Sets the debug level.
*
* @param level The new debug level.
*/
private static void setDebugLevel(int level) {
FreeColDebugger.debugLevel = level;
}
/**
* Checks if the program is in debug mode.
*
* @return True if the program is in debug mode.
*/
public static boolean isInDebugMode() {
return getDebugLevel() > DEBUG_OFF;
}
/**
* Sets the "debug mode" to be active or not.
*
* @param debug Should be <code>true</code> in order to active
* debug mode and <code>false</code> otherwise.
*/
public static void setInDebugMode(boolean debug) {
setDebugLevel((debug) ? DEBUG_FULL : DEBUG_OFF);
}
/**
* Gets the turns to run in debug mode.
*
* @return The turns to run in debug mode.
*/
public static int getDebugRunTurns() {
return FreeColDebugger.debugRunTurns;
}
/**
* Sets the number of turns to run in debug mode.
*
* @param debugRunTurns The new number of debug turns.
*/
public static void setDebugRunTurns(int debugRunTurns) {
FreeColDebugger.debugRunTurns = debugRunTurns;
}
/**
* Gets the debug save file name.
*
* @return The debug save file name.
*/
public static String getDebugRunSave() {
return FreeColDebugger.debugRunSave;
}
/**
* Sets the debug save file name.
*
* @param debugRunSave The new debug save file name.
*/
public static void setDebugRunSave(String debugRunSave) {
FreeColDebugger.debugRunSave = debugRunSave;
}
/**
* Try to complete a debug run if one is happening.
*
* @param freeColClient The <code>FreeColClient</code> of the game.
* @param force Force early completion of a run.
* @return True if a debug run was completed.
*/
public static boolean finishDebugRun(FreeColClient freeColClient,
boolean force) {
if (getDebugRunTurns() < 0) return false; // Not a debug run
if (getDebugRunTurns() > 0 && !force) return false; // Still going
// Zero => signalEndDebugRun was called
setDebugRunTurns(-1);
if (getDebugRunSave() != null) {
FreeColServer fcs = freeColClient.getFreeColServer();
if (fcs != null) {
try {
fcs.saveGame(new File(".", getDebugRunSave()),
freeColClient.getMyPlayer().getName(),
freeColClient.getClientOptions());
} catch (IOException e) {}
}
freeColClient.quit();
}
return true;
}
/**
* Signal that a debug run should complete at the next suitable
* opportunity. Currently called from the server.
*/
public static void signalEndDebugRun() {
if (debugRunTurns > 0) setDebugRunTurns(0);
}
/**
* Handler for log records that include a crash.
*
* @param record The <code>LogRecord</code> with a crash.
*/
public static void handleCrash(LogRecord record) {
if (debugRunSave != null) signalEndDebugRun();
}
/**
* Emergency run time log to use when the normal logging is failing.
* It might as well be here.
*
* @param msg The message to log.
*/
public static void debugLog(String msg) {
try {
new PrintStream(new FileOutputStream("/tmp/freecol.debug", true),
true).println(msg);
} catch (Exception e) {}
}
/**
* Miscellaneous debug helper to get a string representation of
* the current call stack.
*
* @return A stack trace as a string.
*/
public static String stackTraceToString() {
StringBuilder sb = new StringBuilder();
for (StackTraceElement s : Thread.currentThread().getStackTrace()) {
sb.append(s.toString());
sb.append("\n");
}
sb.deleteCharAt(sb.length()-1);
return sb.toString();
}
}