fix: Removes profiling. Streamlines core tick count. (#1948)
### Summary - Removes `Profiling` - Recommend using Visual Studio Performance Profiler, [dotnet-trace](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/dotnet-trace) or [JetBrains dotTrace](https://www.jetbrains.com/profiler/) - Cleans up the core tick count, sampling, etc.
This commit is contained in:
parent
65bc264402
commit
2e6ddcd31a
14 changed files with 80 additions and 949 deletions
|
|
@ -1,427 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Server.Diagnostics;
|
||||
|
||||
namespace Server.Commands
|
||||
{
|
||||
public static class Profiling
|
||||
{
|
||||
public static void Configure()
|
||||
{
|
||||
CommandSystem.Register("DumpTimers", AccessLevel.Administrator, DumpTimers_OnCommand);
|
||||
CommandSystem.Register("CountObjects", AccessLevel.Administrator, CountObjects_OnCommand);
|
||||
CommandSystem.Register("ProfileWorld", AccessLevel.Administrator, ProfileWorld_OnCommand);
|
||||
CommandSystem.Register("TraceInternal", AccessLevel.Administrator, TraceInternal_OnCommand);
|
||||
CommandSystem.Register("TraceExpanded", AccessLevel.Administrator, TraceExpanded_OnCommand);
|
||||
CommandSystem.Register("WriteProfiles", AccessLevel.Administrator, WriteProfiles_OnCommand);
|
||||
CommandSystem.Register("SetProfiles", AccessLevel.Administrator, SetProfiles_OnCommand);
|
||||
}
|
||||
|
||||
[Usage("WriteProfiles")]
|
||||
[Description("Generates a log files containing performance diagnostic information.")]
|
||||
public static void WriteProfiles_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var sw = new StreamWriter("profiles.log", true);
|
||||
sw.WriteLine("# Dump on {0:f}", Core.Now);
|
||||
sw.WriteLine($"# Core profiling for {Core.ProfileTime}");
|
||||
|
||||
sw.WriteLine("# Packet send");
|
||||
BaseProfile.WriteAll(sw, PacketSendProfile.Profiles);
|
||||
sw.WriteLine();
|
||||
|
||||
sw.WriteLine("# Packet receive");
|
||||
BaseProfile.WriteAll(sw, PacketReceiveProfile.Profiles);
|
||||
sw.WriteLine();
|
||||
|
||||
sw.WriteLine("# Timer");
|
||||
BaseProfile.WriteAll(sw, TimerProfile.Profiles);
|
||||
sw.WriteLine();
|
||||
|
||||
sw.WriteLine("# Gump response");
|
||||
BaseProfile.WriteAll(sw, GumpProfile.Profiles);
|
||||
sw.WriteLine();
|
||||
|
||||
sw.WriteLine("# Target response");
|
||||
BaseProfile.WriteAll(sw, TargetProfile.Profiles);
|
||||
sw.WriteLine();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
[Usage("SetProfiles [true | false]"),
|
||||
Description("Enables, disables, or toggles the state of core packet and timer profiling.")]
|
||||
public static void SetProfiles_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
if (e.Length == 1)
|
||||
{
|
||||
Core.Profiling = e.GetBoolean(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Core.Profiling = !Core.Profiling;
|
||||
}
|
||||
|
||||
if (Core.Profiling)
|
||||
{
|
||||
e.Mobile.SendMessage($"Profiling has been enabled.");
|
||||
}
|
||||
else
|
||||
{
|
||||
e.Mobile.SendMessage($"Profiling has been disabled.");
|
||||
}
|
||||
}
|
||||
|
||||
[Usage("DumpTimers"),
|
||||
Description("Generates a log file of all currently executing timers. Used for tracing timer leaks.")]
|
||||
public static void DumpTimers_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var sw = new StreamWriter("timerdump.log", true);
|
||||
Timer.DumpInfo(sw);
|
||||
e.Mobile.SendMessage("Timers dumped to timerdump.log");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
[Usage("CountObjects")]
|
||||
[Description("Generates a log file detailing all item and mobile types in the world.")]
|
||||
public static void CountObjects_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
using (var op = new StreamWriter("objects.log"))
|
||||
{
|
||||
var table = new Dictionary<Type, int>();
|
||||
|
||||
foreach (var item in World.Items.Values)
|
||||
{
|
||||
var type = item.GetType();
|
||||
|
||||
table[type] = (table.TryGetValue(type, out var value) ? value : 0) + 1;
|
||||
}
|
||||
|
||||
var items = table.ToList();
|
||||
table.Clear();
|
||||
|
||||
foreach (var m in World.Mobiles.Values)
|
||||
{
|
||||
var type = m.GetType();
|
||||
|
||||
table[type] = (table.TryGetValue(type, out var value) ? value : 0) + 1;
|
||||
}
|
||||
|
||||
var mobiles = table.ToList();
|
||||
|
||||
items.Sort(new CountSorter());
|
||||
mobiles.Sort(new CountSorter());
|
||||
|
||||
op.WriteLine("# Object count table generated on {0}", Core.Now);
|
||||
op.WriteLine();
|
||||
op.WriteLine();
|
||||
|
||||
op.WriteLine("# Items:");
|
||||
|
||||
items.ForEach(
|
||||
kvp =>
|
||||
op.WriteLine("{0}\t{1:F2}%\t{2}", kvp.Value, 100.0 * kvp.Value / World.Items.Count, kvp.Key)
|
||||
);
|
||||
|
||||
op.WriteLine();
|
||||
op.WriteLine();
|
||||
|
||||
op.WriteLine("#Mobiles:");
|
||||
|
||||
mobiles.ForEach(
|
||||
kvp =>
|
||||
op.WriteLine("{0}\t{1:F2}%\t{2}", kvp.Value, 100.0 * kvp.Value / World.Mobiles.Count, kvp.Key)
|
||||
);
|
||||
}
|
||||
|
||||
e.Mobile.SendMessage("Object table has been generated. See the file : objects.log");
|
||||
}
|
||||
|
||||
[Usage("TraceExpanded")]
|
||||
[Description("Generates a log file describing all items using expanded memory.")]
|
||||
public static void TraceExpanded_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
var typeTable = new Dictionary<Type, int[]>();
|
||||
|
||||
foreach (var item in World.Items.Values)
|
||||
{
|
||||
var flags = item.GetExpandFlags();
|
||||
|
||||
if ((flags & ~(ExpandFlag.TempFlag | ExpandFlag.SaveFlag)) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var itemType = item.GetType();
|
||||
|
||||
do
|
||||
{
|
||||
if (!typeTable.TryGetValue(itemType, out var countTable))
|
||||
{
|
||||
typeTable[itemType] = countTable = new int[9];
|
||||
}
|
||||
|
||||
if ((flags & ExpandFlag.Name) != 0)
|
||||
{
|
||||
++countTable[0];
|
||||
}
|
||||
|
||||
if ((flags & ExpandFlag.Items) != 0)
|
||||
{
|
||||
++countTable[1];
|
||||
}
|
||||
|
||||
if ((flags & ExpandFlag.Bounce) != 0)
|
||||
{
|
||||
++countTable[2];
|
||||
}
|
||||
|
||||
if ((flags & ExpandFlag.Holder) != 0)
|
||||
{
|
||||
++countTable[3];
|
||||
}
|
||||
|
||||
if ((flags & ExpandFlag.Blessed) != 0)
|
||||
{
|
||||
++countTable[4];
|
||||
}
|
||||
|
||||
/*if (( flags & ExpandFlag.TempFlag ) != 0)
|
||||
++countTable[5];
|
||||
|
||||
if (( flags & ExpandFlag.SaveFlag ) != 0)
|
||||
++countTable[6];*/
|
||||
|
||||
if ((flags & ExpandFlag.Weight) != 0)
|
||||
{
|
||||
++countTable[7];
|
||||
}
|
||||
|
||||
if ((flags & ExpandFlag.Spawner) != 0)
|
||||
{
|
||||
++countTable[8];
|
||||
}
|
||||
|
||||
itemType = itemType.BaseType;
|
||||
} while (itemType != typeof(object));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using var op = new StreamWriter("expandedItems.log", true);
|
||||
string[] names =
|
||||
{
|
||||
"Name",
|
||||
"Items",
|
||||
"Bounce",
|
||||
"Holder",
|
||||
"Blessed",
|
||||
"TempFlag",
|
||||
"SaveFlag",
|
||||
"Weight",
|
||||
"Spawner"
|
||||
};
|
||||
|
||||
var list = typeTable.ToList();
|
||||
|
||||
list.Sort(new CountsSorter());
|
||||
|
||||
foreach (var kvp in list)
|
||||
{
|
||||
var countTable = kvp.Value;
|
||||
|
||||
op.WriteLine("# {0}", kvp.Key.FullName);
|
||||
|
||||
for (var i = 0; i < countTable.Length; ++i)
|
||||
{
|
||||
if (countTable[i] > 0)
|
||||
{
|
||||
op.WriteLine("{0}\t{1:N0}", names[i], countTable[i]);
|
||||
}
|
||||
}
|
||||
|
||||
op.WriteLine();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
[Usage("TraceInternal")]
|
||||
[Description("Generates a log file describing all items in the 'internal' map.")]
|
||||
public static void TraceInternal_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
var totalCount = 0;
|
||||
var table = new Dictionary<Type, int[]>();
|
||||
|
||||
foreach (var item in World.Items.Values)
|
||||
{
|
||||
if (item.Parent != null || item.Map != Map.Internal)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
++totalCount;
|
||||
|
||||
var type = item.GetType();
|
||||
|
||||
if (table.TryGetValue(type, out var parms))
|
||||
{
|
||||
parms[0]++;
|
||||
parms[1] += item.Amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
table[type] = new[] { 1, item.Amount };
|
||||
}
|
||||
}
|
||||
|
||||
using var op = new StreamWriter("internal.log");
|
||||
op.WriteLine("# {0} items found", totalCount);
|
||||
op.WriteLine("# {0} different types", table.Count);
|
||||
op.WriteLine();
|
||||
op.WriteLine();
|
||||
op.WriteLine("Type\t\tCount\t\tAmount\t\tAvg. Amount");
|
||||
|
||||
foreach (var de in table)
|
||||
{
|
||||
var parms = de.Value;
|
||||
|
||||
op.WriteLine("{0}\t\t{1}\t\t{2}\t\t{3:F2}", de.Key.Name, parms[0], parms[1], (double)parms[1] / parms[0]);
|
||||
}
|
||||
}
|
||||
|
||||
[Usage("ProfileWorld"),
|
||||
Description("Prints the amount of data serialized for every object type in your world file.")]
|
||||
public static void ProfileWorld_OnCommand(CommandEventArgs e)
|
||||
{
|
||||
ProfileWorld("items", "worldprofile_items.log");
|
||||
ProfileWorld("mobiles", "worldprofile_mobiles.log");
|
||||
}
|
||||
|
||||
public static void ProfileWorld(string type, string opFile)
|
||||
{
|
||||
try
|
||||
{
|
||||
var types = new List<Type>();
|
||||
|
||||
using (var bin = new BinaryReader(
|
||||
new FileStream(
|
||||
string.Format("Saves/{0}/{0}.tdb", type),
|
||||
FileMode.Open,
|
||||
FileAccess.Read,
|
||||
FileShare.Read
|
||||
)
|
||||
))
|
||||
{
|
||||
var count = bin.ReadInt32();
|
||||
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
types.Add(AssemblyHandler.FindTypeByFullName(bin.ReadString()));
|
||||
}
|
||||
}
|
||||
|
||||
long total = 0;
|
||||
|
||||
var table = new Dictionary<Type, int>();
|
||||
|
||||
using (var bin = new BinaryReader(
|
||||
new FileStream(
|
||||
string.Format("Saves/{0}/{0}.idx", type),
|
||||
FileMode.Open,
|
||||
FileAccess.Read,
|
||||
FileShare.Read
|
||||
)
|
||||
))
|
||||
{
|
||||
var count = bin.ReadInt32();
|
||||
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
var typeID = bin.ReadInt32();
|
||||
var serial = bin.ReadInt32();
|
||||
var pos = bin.ReadInt64();
|
||||
var length = bin.ReadInt32();
|
||||
var objType = types[typeID];
|
||||
|
||||
while (objType != null && objType != typeof(object))
|
||||
{
|
||||
table[objType] = length + (table.TryGetValue(objType, out var value) ? value : 0);
|
||||
objType = objType.BaseType;
|
||||
total += length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var list = table.ToList();
|
||||
|
||||
list.Sort(new CountSorter());
|
||||
|
||||
using var op = new StreamWriter(opFile);
|
||||
op.WriteLine("# Profile of world {0}", type);
|
||||
op.WriteLine("# Generated on {0}", Core.Now);
|
||||
op.WriteLine();
|
||||
op.WriteLine();
|
||||
|
||||
list.ForEach(
|
||||
kvp =>
|
||||
op.WriteLine("{0}\t{1:F2}%\t{2}", kvp.Value, 100.0 * kvp.Value / total, kvp.Key)
|
||||
);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private class CountSorter : IComparer<KeyValuePair<Type, int>>
|
||||
{
|
||||
public int Compare(KeyValuePair<Type, int> x, KeyValuePair<Type, int> y)
|
||||
{
|
||||
var aCount = x.Value;
|
||||
var bCount = y.Value;
|
||||
|
||||
var v = -aCount.CompareTo(bCount);
|
||||
|
||||
return v != 0 ? v : string.CompareOrdinal(x.Key.FullName, y.Key.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
private class CountsSorter : IComparer<KeyValuePair<Type, int[]>>
|
||||
{
|
||||
public int Compare(KeyValuePair<Type, int[]> x, KeyValuePair<Type, int[]> y)
|
||||
{
|
||||
var aCount = 0;
|
||||
foreach (var val in x.Value)
|
||||
{
|
||||
aCount += val;
|
||||
}
|
||||
|
||||
var bCount = 0;
|
||||
foreach (var val in y.Value)
|
||||
{
|
||||
bCount += val;
|
||||
}
|
||||
|
||||
var v = -aCount.CompareTo(bCount);
|
||||
|
||||
return v != 0 ? v : string.CompareOrdinal(x.Key.FullName, y.Key.FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Server.Diagnostics;
|
||||
|
||||
public class GumpProfile : BaseProfile
|
||||
{
|
||||
private static readonly Dictionary<Type, GumpProfile> _profiles = new();
|
||||
|
||||
public GumpProfile(Type type) : base(type.FullName)
|
||||
{
|
||||
}
|
||||
|
||||
public static IEnumerable<GumpProfile> Profiles => _profiles.Values;
|
||||
|
||||
public static GumpProfile Acquire(Type type)
|
||||
{
|
||||
if (!Core.Profiling)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!_profiles.TryGetValue(type, out var prof))
|
||||
{
|
||||
_profiles.Add(type, prof = new GumpProfile(type));
|
||||
}
|
||||
|
||||
return prof;
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,6 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
using Server.Diagnostics;
|
||||
using Server.Engines.Virtues;
|
||||
using Server.Exceptions;
|
||||
using Server.Mobiles;
|
||||
|
|
@ -157,10 +156,6 @@ public static partial class GumpSystem
|
|||
|
||||
Remove(state, baseGump);
|
||||
|
||||
var prof = GumpProfile.Acquire(baseGump.GetType());
|
||||
|
||||
prof?.Start();
|
||||
|
||||
var relayInfo = new RelayInfo(
|
||||
buttonId,
|
||||
switches,
|
||||
|
|
@ -169,8 +164,6 @@ public static partial class GumpSystem
|
|||
textBlock
|
||||
);
|
||||
baseGump.OnResponse(state, relayInfo);
|
||||
|
||||
prof?.Finish();
|
||||
}
|
||||
|
||||
if (typeId == 461)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
*************************************************************************/
|
||||
|
||||
using System.Buffers;
|
||||
using Server.Diagnostics;
|
||||
using Server.Targeting;
|
||||
|
||||
namespace Server.Network;
|
||||
|
|
@ -52,95 +51,83 @@ public static class IncomingTargetingPackets
|
|||
return;
|
||||
}
|
||||
|
||||
var prof = TargetProfile.Acquire(t.GetType());
|
||||
prof?.Start();
|
||||
|
||||
try
|
||||
if (x == -1 && y == -1 && !serial.IsValid)
|
||||
{
|
||||
if (x == -1 && y == -1 && !serial.IsValid)
|
||||
{
|
||||
// User pressed escape
|
||||
t.Cancel(from, TargetCancelType.Canceled);
|
||||
}
|
||||
else if (t.TargetID == targetID)
|
||||
{
|
||||
object toTarget;
|
||||
// User pressed escape
|
||||
t.Cancel(from, TargetCancelType.Canceled);
|
||||
}
|
||||
else if (t.TargetID == targetID)
|
||||
{
|
||||
object toTarget;
|
||||
|
||||
if (type == 1)
|
||||
if (type == 1)
|
||||
{
|
||||
if (graphic == 0)
|
||||
{
|
||||
if (graphic == 0)
|
||||
toTarget = new LandTarget(new Point3D(x, y, z), from.Map);
|
||||
}
|
||||
else
|
||||
{
|
||||
var map = from.Map;
|
||||
|
||||
if (map == null || map == Map.Internal)
|
||||
{
|
||||
toTarget = new LandTarget(new Point3D(x, y, z), from.Map);
|
||||
t.Cancel(from, TargetCancelType.Canceled);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
var map = from.Map;
|
||||
if (state.HighSeas)
|
||||
{
|
||||
var id = TileData.ItemTable[graphic & TileData.MaxItemValue];
|
||||
if (id.Surface)
|
||||
{
|
||||
z -= id.Height;
|
||||
}
|
||||
}
|
||||
|
||||
if (map == null || map == Map.Internal)
|
||||
int hue = 0;
|
||||
var valid = false;
|
||||
|
||||
var eable = t.DisallowMultis
|
||||
? map.Tiles.GetStaticTiles(x, y)
|
||||
: map.Tiles.GetStaticAndMultiTiles(x, y);
|
||||
|
||||
foreach (var tile in eable)
|
||||
{
|
||||
if (tile.Z == z && tile.ID == graphic)
|
||||
{
|
||||
valid = true;
|
||||
hue = tile.Hue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
t.Cancel(from, TargetCancelType.Canceled);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (state.HighSeas)
|
||||
{
|
||||
var id = TileData.ItemTable[graphic & TileData.MaxItemValue];
|
||||
if (id.Surface)
|
||||
{
|
||||
z -= id.Height;
|
||||
}
|
||||
}
|
||||
|
||||
int hue = 0;
|
||||
var valid = false;
|
||||
|
||||
var eable = t.DisallowMultis
|
||||
? map.Tiles.GetStaticTiles(x, y)
|
||||
: map.Tiles.GetStaticAndMultiTiles(x, y);
|
||||
|
||||
foreach (var tile in eable)
|
||||
{
|
||||
if (tile.Z == z && tile.ID == graphic)
|
||||
{
|
||||
valid = true;
|
||||
hue = tile.Hue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
t.Cancel(from, TargetCancelType.Canceled);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
toTarget = new StaticTarget(new Point3D(x, y, z), graphic, hue);
|
||||
}
|
||||
}
|
||||
toTarget = new StaticTarget(new Point3D(x, y, z), graphic, hue);
|
||||
}
|
||||
}
|
||||
else if (serial.IsMobile)
|
||||
{
|
||||
toTarget = World.FindMobile(serial);
|
||||
}
|
||||
else if (serial.IsItem)
|
||||
{
|
||||
toTarget = World.FindItem(serial);
|
||||
}
|
||||
else
|
||||
{
|
||||
t.Cancel(from, TargetCancelType.Canceled);
|
||||
return;
|
||||
}
|
||||
|
||||
t.Invoke(from, toTarget);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
prof?.Finish();
|
||||
else if (serial.IsMobile)
|
||||
{
|
||||
toTarget = World.FindMobile(serial);
|
||||
}
|
||||
else if (serial.IsItem)
|
||||
{
|
||||
toTarget = World.FindItem(serial);
|
||||
}
|
||||
else
|
||||
{
|
||||
t.Cancel(from, TargetCancelType.Canceled);
|
||||
return;
|
||||
}
|
||||
|
||||
t.Invoke(from, toTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue