You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Debug.java 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. /*
  2. * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. * SOFTWARE.
  21. */
  22. package com.dmdirc.commandparser.commands.global;
  23. import com.dmdirc.Main;
  24. import com.dmdirc.Server;
  25. import com.dmdirc.commandparser.CommandArguments;
  26. import com.dmdirc.commandparser.CommandManager;
  27. import com.dmdirc.commandparser.commands.GlobalCommand;
  28. import com.dmdirc.commandparser.commands.IntelligentCommand;
  29. import com.dmdirc.config.ConfigManager;
  30. import com.dmdirc.config.Identity;
  31. import com.dmdirc.config.IdentityManager;
  32. import com.dmdirc.logger.ErrorLevel;
  33. import com.dmdirc.logger.Logger;
  34. import com.dmdirc.plugins.PluginManager;
  35. import com.dmdirc.plugins.Service;
  36. import com.dmdirc.plugins.ServiceProvider;
  37. import com.dmdirc.ui.input.AdditionalTabTargets;
  38. import com.dmdirc.ui.interfaces.InputWindow;
  39. import com.dmdirc.ui.messages.Styliser;
  40. import com.dmdirc.updater.UpdateChecker;
  41. import java.io.Serializable;
  42. import java.util.Comparator;
  43. import java.util.List;
  44. import java.util.Map;
  45. import java.util.Map.Entry;
  46. import java.util.TreeSet;
  47. /**
  48. * Provides various handy ways to test or debug the client.
  49. *
  50. * @author Chris
  51. */
  52. public class Debug extends GlobalCommand implements IntelligentCommand {
  53. /**
  54. * Creates a new instance of Debug.
  55. */
  56. public Debug() {
  57. CommandManager.registerCommand(this);
  58. }
  59. /** {@inheritDoc} */
  60. @Override
  61. public void execute(final InputWindow origin, final boolean isSilent,
  62. final CommandArguments args) {
  63. if (args.getArguments().length == 0) {
  64. showUsage(origin, isSilent, "debug", "<debug command> [options]");
  65. } else if ("error".equals(args.getArguments()[0])) {
  66. doError(args.getArguments());
  67. } else if ("showraw".equals(args.getArguments()[0])) {
  68. doShowRaw(origin, isSilent);
  69. } else if ("configstats".equals(args.getArguments()[0])) {
  70. doConfigStats(origin, isSilent);
  71. } else if ("configinfo".equals(args.getArguments()[0])) {
  72. doConfigInfo(origin, isSilent);
  73. } else if ("globalconfiginfo".equals(args.getArguments()[0])) {
  74. doGlobalConfigInfo(origin, isSilent);
  75. } else if ("colourspam".equals(args.getArguments()[0])) {
  76. doColourSpam(origin, isSilent);
  77. } else if ("meminfo".equals(args.getArguments()[0])) {
  78. doMemInfo(origin, isSilent);
  79. } else if ("rungc".equals(args.getArguments()[0])) {
  80. doGarbage(origin, isSilent);
  81. } else if ("threads".equals(args.getArguments()[0])) {
  82. doThreads(origin, isSilent);
  83. } else if ("forceupdate".equals(args.getArguments()[0])) {
  84. doForceUpdate();
  85. } else if ("serverinfo".equals(args.getArguments()[0])) {
  86. doServerInfo(origin, isSilent);
  87. } else if ("serverstate".equals(args.getArguments()[0])) {
  88. doServerState(origin, isSilent);
  89. } else if ("benchmark".equals(args.getArguments()[0])) {
  90. doBenchmark(origin);
  91. } else if ("services".equals(args.getArguments()[0])) {
  92. doServices(origin, isSilent, args.getArguments());
  93. } else if ("firstrun".equals(args.getArguments()[0])) {
  94. Main.getUI().showFirstRunWizard();
  95. } else if ("migration".equals(args.getArguments()[0])) {
  96. Main.getUI().showMigrationWizard();
  97. } else if ("notify".equals(args.getArguments()[0])) {
  98. sendLine(origin, isSilent, FORMAT_OUTPUT, "Current notification colour is: "
  99. + origin.getContainer().getNotification());
  100. } else {
  101. sendLine(origin, isSilent, FORMAT_ERROR, "Unknown debug action.");
  102. }
  103. }
  104. /**
  105. * Generates a fake error.
  106. *
  107. * @param args The arguments that were passed to the command
  108. */
  109. private void doError(final String ... args) {
  110. ErrorLevel el = ErrorLevel.HIGH;
  111. if (args.length > 2) {
  112. final String level = args[2];
  113. if (level.equals("low")) {
  114. el = ErrorLevel.LOW;
  115. } else if (level.equals("medium")) {
  116. el = ErrorLevel.MEDIUM;
  117. } else if (level.equals("fatal")) {
  118. el = ErrorLevel.FATAL;
  119. } else if (level.equals("unknown")) {
  120. el = ErrorLevel.UNKNOWN;
  121. }
  122. }
  123. if (args.length > 1 && args[1].equals("user")) {
  124. Logger.userError(el, "Debug error message");
  125. } else {
  126. Logger.appError(el, "Debug error message", new Exception());
  127. }
  128. }
  129. /**
  130. * Attempts to show the server's raw window.
  131. *
  132. * @param origin The window this command was executed in
  133. * @param isSilent Whether this command has been silenced or not
  134. */
  135. private void doShowRaw(final InputWindow origin, final boolean isSilent) {
  136. if (origin == null || origin.getContainer() == null
  137. || origin.getContainer().getServer() == null) {
  138. sendLine(origin, isSilent, FORMAT_ERROR, "Cannot show raw window here.");
  139. } else {
  140. origin.getContainer().getServer().addRaw();
  141. }
  142. }
  143. /**
  144. * Shows stats related to the config system.
  145. *
  146. * @param origin The window this command was executed in
  147. * @param isSilent Whether this command has been silenced or not
  148. */
  149. private void doConfigStats(final InputWindow origin, final boolean isSilent) {
  150. final TreeSet<Entry<String, Integer>> sortedStats =
  151. new TreeSet<Entry<String, Integer>>(new ValueComparator());
  152. sortedStats.addAll(ConfigManager.getStats().entrySet());
  153. for (Map.Entry<String, Integer> entry : sortedStats) {
  154. sendLine(origin, isSilent, FORMAT_OUTPUT,
  155. entry.getKey() + " - " + entry.getValue());
  156. }
  157. }
  158. /**
  159. * Shows memory usage information.
  160. *
  161. * @param origin The window this command was executed in
  162. * @param isSilent Whether this command has been silenced or not
  163. */
  164. private void doMemInfo(final InputWindow origin, final boolean isSilent) {
  165. sendLine(origin, isSilent, FORMAT_OUTPUT, "Total Memory: "
  166. + Runtime.getRuntime().totalMemory());
  167. sendLine(origin, isSilent, FORMAT_OUTPUT, "Free Memory: "
  168. + Runtime.getRuntime().freeMemory());
  169. sendLine(origin, isSilent, FORMAT_OUTPUT, "Used Memory: "
  170. + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()));
  171. }
  172. /**
  173. * Outputs 100 lines containing various colours.
  174. *
  175. * @param origin The window this command was executed in
  176. * @param isSilent Whether this command has been silenced or not
  177. */
  178. private void doColourSpam(final InputWindow origin, final boolean isSilent) {
  179. for (int i = 0; i < 100; i++) {
  180. sendLine(origin, isSilent, FORMAT_OUTPUT, ((char) 3) + "5Colour! "
  181. + ((char) 3) + "6Colour! " + ((char) 3) + "7Colour! "
  182. + ((char) 3) + "6Colour! " + ((char) 3) + "7Colour! "
  183. + ((char) 3) + "6Colour! " + ((char) 3) + "7Colour! "
  184. + ((char) 3) + "6Colour! " + ((char) 3) + "7Colour! ");
  185. }
  186. }
  187. /**
  188. * Manually runs the garbage collector.
  189. *
  190. * @param origin The window this command was executed in
  191. * @param isSilent Whether this command has been silenced or not
  192. */
  193. private void doGarbage(final InputWindow origin, final boolean isSilent) {
  194. System.gc();
  195. sendLine(origin, isSilent, FORMAT_OUTPUT, "Invoked garbage collector.");
  196. }
  197. /**
  198. * Shows information about the config manager.
  199. *
  200. * @param origin The window this command was executed in
  201. * @param isSilent Whether this command has been silenced or not
  202. */
  203. private void doConfigInfo(final InputWindow origin, final boolean isSilent) {
  204. for (Identity source : origin.getConfigManager().getSources()) {
  205. sendLine(origin, isSilent, FORMAT_OUTPUT, source.getTarget() + " - "
  206. + source + "(" + source.getTarget().getOrder() + ")");
  207. }
  208. }
  209. /**
  210. * Shows information about the global config manager.
  211. *
  212. * @param origin The window this command was executed in
  213. * @param isSilent Whether this command has been silenced or not
  214. */
  215. private void doGlobalConfigInfo(final InputWindow origin, final boolean isSilent) {
  216. for (Identity source : IdentityManager.getGlobalConfig().getSources()) {
  217. sendLine(origin, isSilent, FORMAT_OUTPUT, source.getTarget() + " - "
  218. + source + "(" + source.getTarget().getOrder() + ")");
  219. }
  220. }
  221. /**
  222. * Forces the update checker to check for updates.
  223. */
  224. private void doForceUpdate() {
  225. new Thread(new UpdateChecker(), "Forced update checker").start();
  226. }
  227. /**
  228. * Shows information about active threads.
  229. *
  230. * @param origin The window this command was executed in
  231. * @param isSilent Whether this command has been silenced or not
  232. */
  233. private void doThreads(final InputWindow origin, final boolean isSilent) {
  234. for (Entry<Thread, StackTraceElement[]> thread: Thread.getAllStackTraces().entrySet()) {
  235. sendLine(origin, isSilent, FORMAT_OUTPUT, Styliser.CODE_BOLD
  236. + thread.getKey().getName());
  237. for (StackTraceElement element : thread.getValue()) {
  238. sendLine(origin, isSilent, FORMAT_OUTPUT, Styliser.CODE_FIXED
  239. + " " + element.toString());
  240. }
  241. }
  242. }
  243. /**
  244. * Shows information about the current server's state.
  245. *
  246. * @param origin The window this command was executed in
  247. * @param isSilent Whether this command has been silenced or not
  248. */
  249. private void doServerState(final InputWindow origin, final boolean isSilent) {
  250. if (origin.getContainer().getServer() == null) {
  251. sendLine(origin, isSilent, FORMAT_ERROR, "This window isn't connected to a server");
  252. } else {
  253. final Server server = origin.getContainer().getServer();
  254. sendLine(origin, isSilent, FORMAT_OUTPUT, server.getStatus().getTransitionHistory());
  255. }
  256. }
  257. /**
  258. * Shows information about the current server.
  259. *
  260. * @param origin The window this command was executed in
  261. * @param isSilent Whether this command has been silenced or not
  262. */
  263. private void doServerInfo(final InputWindow origin, final boolean isSilent) {
  264. if (origin.getContainer().getServer() == null) {
  265. sendLine(origin, isSilent, FORMAT_ERROR, "This window isn't connected to a server");
  266. } else {
  267. final Server server = origin.getContainer().getServer();
  268. sendLine(origin, isSilent, FORMAT_OUTPUT, "Server name: " + server.getName());
  269. sendLine(origin, isSilent, FORMAT_OUTPUT, "Actual name: "
  270. + server.getParser().getServerName());
  271. sendLine(origin, isSilent, FORMAT_OUTPUT, "Network: " + server.getNetwork());
  272. sendLine(origin, isSilent, FORMAT_OUTPUT, "IRCd: "
  273. + server.getParser().getIRCD(false) + " - "
  274. + server.getParser().getIRCD(true));
  275. sendLine(origin, isSilent, FORMAT_OUTPUT, "Modes: "
  276. + server.getParser().getBoolChanModes() + " "
  277. + server.getParser().getListChanModes() + " "
  278. + server.getParser().getSetOnlyChanModes() + " "
  279. + server.getParser().getSetUnsetChanModes());
  280. }
  281. }
  282. /**
  283. * Benchmarks the textpane.
  284. *
  285. * @param origin The window this command was executed in
  286. */
  287. private void doBenchmark(final InputWindow origin) {
  288. long[] results = new long[10];
  289. for (int i = 0; i < results.length; i++) {
  290. final long start = System.nanoTime();
  291. for (int j = 0; j < 5000; j++) {
  292. origin.addLine(FORMAT_OUTPUT, "This is a benchmark. Lorem ipsum doler...");
  293. }
  294. final long end = System.nanoTime();
  295. results[i] = end - start;
  296. }
  297. for (int i = 0; i < results.length; i++) {
  298. origin.addLine(FORMAT_OUTPUT, "Iteration " + i + ": " + results[i]
  299. + " nanoseconds.");
  300. }
  301. }
  302. /**
  303. * Shows information about all the current services available to plugins.
  304. *
  305. * @param origin The window this command was executed in
  306. * @param isSilent Whether this command has been silenced or not
  307. * @param args The arguments that were passed to the command
  308. */
  309. private void doServices(final InputWindow origin, final boolean isSilent,
  310. final String[] args) {
  311. sendLine(origin, isSilent, FORMAT_OUTPUT, "Available Services:");
  312. for (Service service : PluginManager.getPluginManager().getAllServices()) {
  313. sendLine(origin, isSilent, FORMAT_OUTPUT, " " + service.toString());
  314. if (args.length > 1 && args[1].equals("full")) {
  315. for (ServiceProvider provider : service.getProviders()) {
  316. sendLine(origin, isSilent, FORMAT_OUTPUT, " "
  317. + provider.getProviderName() + " [Active: "
  318. + provider.isActive() + "]");
  319. }
  320. }
  321. }
  322. }
  323. /** {@inheritDoc} */
  324. @Override
  325. public String getName() {
  326. return "debug";
  327. }
  328. /** {@inheritDoc} */
  329. @Override
  330. public boolean showInHelp() {
  331. return false;
  332. }
  333. /** {@inheritDoc} */
  334. @Override
  335. public String getHelp() {
  336. return null;
  337. }
  338. /** {@inheritDoc} */
  339. @Override
  340. public AdditionalTabTargets getSuggestions(final int arg, final List<String> previousArgs) {
  341. final AdditionalTabTargets res = new AdditionalTabTargets();
  342. res.excludeAll();
  343. if (arg == 0) {
  344. res.add("error");
  345. res.add("showraw");
  346. res.add("colourspam");
  347. res.add("configstats");
  348. res.add("meminfo");
  349. res.add("rungc");
  350. res.add("configinfo");
  351. res.add("globalconfiginfo");
  352. res.add("forceupdate");
  353. res.add("serverinfo");
  354. res.add("serverstate");
  355. res.add("threads");
  356. res.add("benchmark");
  357. res.add("firstrun");
  358. res.add("migration");
  359. res.add("notify");
  360. res.add("services");
  361. } else if (arg == 1 && "error".equals(previousArgs.get(0))) {
  362. res.add("user");
  363. res.add("app");
  364. } else if (arg == 1 && "services".equals(previousArgs.get(0))) {
  365. res.add("full");
  366. } else if (arg == 2 && "error".equals(previousArgs.get(0))) {
  367. res.add("low");
  368. res.add("medium");
  369. res.add("high");
  370. res.add("fatal");
  371. res.add("unknown");
  372. }
  373. return res;
  374. }
  375. /** Reverse value comparator for a map entry. */
  376. private static class ValueComparator implements
  377. Comparator<Entry<String, Integer>>, Serializable {
  378. /**
  379. * A version number for this class. It should be changed whenever the
  380. * class structure is changed (or anything else that would prevent
  381. * serialized objects being unserialized with the new class).
  382. */
  383. private static final long serialVersionUID = 1;
  384. /** Instantiates a new ValueComparator. */
  385. public ValueComparator() {
  386. super();
  387. }
  388. /** {@inheritDoc} */
  389. @Override
  390. public int compare(final Entry<String, Integer> o1,
  391. final Entry<String, Integer> o2) {
  392. int returnValue = o1.getValue().compareTo(o2.getValue()) * -1;
  393. if (returnValue == 0) {
  394. returnValue = o1.getKey().compareToIgnoreCase(o2.getKey());
  395. }
  396. return returnValue;
  397. }
  398. }
  399. }