001    package org.bukkit.command.defaults;
002    
003    import java.io.File;
004    import java.io.IOException;
005    import java.io.PrintStream;
006    import java.util.ArrayList;
007    import java.util.List;
008    
009    import org.apache.commons.lang.Validate;
010    import org.bukkit.Bukkit;
011    import org.bukkit.ChatColor;
012    import org.bukkit.command.CommandSender;
013    import org.bukkit.event.Event;
014    import org.bukkit.event.HandlerList;
015    import org.bukkit.plugin.Plugin;
016    import org.bukkit.plugin.RegisteredListener;
017    import org.bukkit.plugin.TimedRegisteredListener;
018    import org.bukkit.util.StringUtil;
019    
020    import com.google.common.collect.ImmutableList;
021    
022    public class TimingsCommand extends BukkitCommand {
023        private static final List<String> TIMINGS_SUBCOMMANDS = ImmutableList.of("merged", "reset", "separate");
024    
025        public TimingsCommand(String name) {
026            super(name);
027            this.description = "Records timings for all plugin events";
028            this.usageMessage = "/timings <reset|merged|separate>";
029            this.setPermission("bukkit.command.timings");
030        }
031    
032        @Override
033        public boolean execute(CommandSender sender, String currentAlias, String[] args) {
034            if (!testPermission(sender)) return true;
035            if (args.length != 1)  {
036                sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage);
037                return false;
038            }
039            if (!sender.getServer().getPluginManager().useTimings()) {
040                sender.sendMessage("Please enable timings by setting \"settings.plugin-profiling\" to true in bukkit.yml");
041                return true;
042            }
043    
044            boolean separate = "separate".equals(args[0]);
045            if ("reset".equals(args[0])) {
046                for (HandlerList handlerList : HandlerList.getHandlerLists()) {
047                    for (RegisteredListener listener : handlerList.getRegisteredListeners()) {
048                        if (listener instanceof TimedRegisteredListener) {
049                            ((TimedRegisteredListener)listener).reset();
050                        }
051                    }
052                }
053                sender.sendMessage("Timings reset");
054            } else if ("merged".equals(args[0]) || separate) {
055    
056                int index = 0;
057                int pluginIdx = 0;
058                File timingFolder = new File("timings");
059                timingFolder.mkdirs();
060                File timings = new File(timingFolder, "timings.txt");
061                File names = null;
062                while (timings.exists()) timings = new File(timingFolder, "timings" + (++index) + ".txt");
063                PrintStream fileTimings = null;
064                PrintStream fileNames = null;
065                try {
066                    fileTimings = new PrintStream(timings);
067                    if (separate) {
068                        names = new File(timingFolder, "names" + index + ".txt");
069                        fileNames = new PrintStream(names);
070                    }
071                    for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) {
072                        pluginIdx++;
073                        long totalTime = 0;
074                        if (separate) {
075                            fileNames.println(pluginIdx + " " + plugin.getDescription().getFullName());
076                            fileTimings.println("Plugin " + pluginIdx);
077                        }
078                        else fileTimings.println(plugin.getDescription().getFullName());
079                        for (RegisteredListener listener : HandlerList.getRegisteredListeners(plugin)) {
080                            if (listener instanceof TimedRegisteredListener) {
081                                TimedRegisteredListener trl = (TimedRegisteredListener) listener;
082                                long time = trl.getTotalTime();
083                                int count = trl.getCount();
084                                if (count == 0) continue;
085                                long avg = time / count;
086                                totalTime += time;
087                                Class<? extends Event> eventClass = trl.getEventClass();
088                                if (count > 0 && eventClass != null) {
089                                    fileTimings.println("    " + eventClass.getSimpleName() + (trl.hasMultiple() ? " (and sub-classes)" : "") + " Time: " + time + " Count: " + count + " Avg: " + avg);
090                                }
091                            }
092                        }
093                        fileTimings.println("    Total time " + totalTime + " (" + totalTime / 1000000000 + "s)");
094                    }
095                    sender.sendMessage("Timings written to " + timings.getPath());
096                    if (separate) sender.sendMessage("Names written to " + names.getPath());
097                } catch (IOException e) {
098                } finally {
099                    if (fileTimings != null) {
100                        fileTimings.close();
101                    }
102                    if (fileNames != null) {
103                        fileNames.close();
104                    }
105                }
106            }
107            return true;
108        }
109    
110        @Override
111        public List<String> tabComplete(CommandSender sender, String alias, String[] args) {
112            Validate.notNull(sender, "Sender cannot be null");
113            Validate.notNull(args, "Arguments cannot be null");
114            Validate.notNull(alias, "Alias cannot be null");
115    
116            if (args.length == 1) {
117                return StringUtil.copyPartialMatches(args[0], TIMINGS_SUBCOMMANDS, new ArrayList<String>(TIMINGS_SUBCOMMANDS.size()));
118            }
119            return ImmutableList.of();
120        }
121    }