public static void ProfilePatch()
        {
            var patches = Harmony.GetAllPatchedMethods().ToList();

            var filteredTranspilers = patches
                                      .Where(m => Harmony.GetPatchInfo(m).Transpilers.Any(p => Utility.IsNotAnalyzerPatch(p.owner) && !TranspilerMethodUtility.PatchedMeths.Contains(m)))
                                      .ToList();

            TranspilerMethodUtility.PatchedMeths.AddRange(filteredTranspilers);

            foreach (var meth in filteredTranspilers)
            {
                try
                {
                    Modbase.Harmony.Patch(meth, transpiler: TranspilerMethodUtility.TranspilerProfiler);
                }
                catch (Exception e)
                {
#if DEBUG
                    ThreadSafeLogger.Error($"[Analyzer] Failed to patch transpiler, failed with the message {e.Message}");
#endif
#if NDEBUG
                    if (Settings.verboseLogging)
                    {
                        ThreadSafeLogger.Error($"[Analyzer] Failed to patch transpiler {meth.DeclaringType.FullName + ":" + meth.Name}, failed with the message {e.Message}");
                    }
#endif
                }
            }
        }
        public static void RegisterHarmonyId(string harmonyid, Assembly assembly)
        {
            if (harmonyIds.TryGetValue(harmonyid, out var asm))
            {
                mods.TryGetValue(asm, out var asmMod);
                mods.TryGetValue(assembly, out var assemblyMod);

                // Doesn't matter a whole lot
                if (asmMod == assemblyMod)
                {
                    return;
                }

                var blameString = $"This is a Harmony ID configuration issue with {asmMod} and {assemblyMod}. Please report that they are instantiating harmony with the same ID.";

                ThreadSafeLogger.Error($"The HarmonyID {harmonyid} has been loaded twice. It is associated with both: \n{asm.FullName} and {assembly.FullName}\n{blameString}\n\nEnable verbose logging and restart to view the associated callstack(s)");
                if (Settings.verboseLogging)
                {
                    ThreadSafeLogger.Error($"Initial Trace: {GetStackTraceString(harmonyIds_d[harmonyid], out _)}");
                    ThreadSafeLogger.Error($"Current Trace: {GetStackTraceString(new StackTrace(1, false), out _)}");
                }
            }
            else
            {
                harmonyIds.Add(harmonyid, assembly);
                if (Settings.verboseLogging)
                {
                    harmonyIds_d.Add(harmonyid, new StackTrace(1, false));
                }
            }
        }
示例#3
0
        public static void AddEntry(string name, Category category)
        {
            Type myType = null;

            if (types.ContainsKey(name))
            {
                myType = types[name];
            }
            else
            {
                myType = DynamicTypeBuilder.CreateType(name, null);
                types.Add(name, myType);
            }

#if DEBUG
            ThreadSafeLogger.Message($"Adding entry {name} into the category {category}");
#endif
            var entry = Entry.Create(name, category, myType, true, true);

            if (Tab(category).entries.ContainsKey(entry))
            {
                ThreadSafeLogger.Error($"Attempting to re-add entry {name} into the category {category}");
            }
            else
            {
                Tab(category).entries.Add(entry, myType);
            }
        }
        public static FileHeader ReadHeader(BinaryReader reader)
        {
            var fileHeader = new FileHeader()
            {
                MAGIC                 = reader.ReadInt32(),
                scribingVer           = reader.ReadInt32(),
                methodName            = reader.ReadString(),
                name                  = reader.ReadString(),
                entryPerCall          = reader.ReadBoolean(),
                onlyEntriesWithValues = reader.ReadBoolean(),
                entries               = reader.ReadInt32(),
                targetEntries         = reader.ReadInt32()
            };

            if (fileHeader.MAGIC == ENTRY_FILE_MAGIC)
            {
                return(fileHeader);
            }

            ThreadSafeLogger.Error($"Loaded header has an invalid MAGIC number, this indicates disk corruption");
            return(new FileHeader()
            {
                MAGIC = -1
            });                                     // magic = -1 is an error value.
        }
示例#5
0
        private static IEnumerable <CodeInstruction> Transpiler(MethodBase __originalMethod, IEnumerable <CodeInstruction> codeInstructions)
        {
            var enumerable = codeInstructions.ToList();

            try
            {
                List <CodeInstruction> instructions = enumerable;

                for (int i = 0; i < instructions.Count; i++)
                {
                    if (!IsFunctionCall(instructions[i].opcode))
                    {
                        continue;
                    }
                    if (!(instructions[i].operand is MethodInfo meth))
                    {
                        continue;
                    }

                    // Check for constrained
                    if (i != 0 && instructions[i - 1].opcode == OpCodes.Constrained)
                    {
                        continue;
                    }
                    // Make sure it is not an analyzer profiling method
                    if (meth.DeclaringType.FullName.Contains("Analyzer.Profiling"))
                    {
                        continue;
                    }

                    var key   = Utility.GetMethodKey(meth);
                    var index = MethodInfoCache.AddMethod(key, meth);

                    var inst = MethodTransplanting.ReplaceMethodInstruction(
                        instructions[i],
                        key,
                        GUIController.types[__originalMethod.DeclaringType + ":" + __originalMethod.Name + "-int"],
                        index);

                    instructions[i] = inst;
                }

                return(instructions);
            }
            catch (Exception e)
            {
#if DEBUG
                ThreadSafeLogger.Error($"Failed to patch the internal method {__originalMethod.DeclaringType.FullName}:{__originalMethod.Name}, failed with the error " + e.Message);
#else
                if (Settings.verboseLogging)
                {
                    ThreadSafeLogger.Warning($"Failed to patch the internal method {__originalMethod.DeclaringType.FullName}:{__originalMethod.Name}, failed with the error " + e.Message);
                }
#endif

                return(enumerable);
            }
        }
        private static void ExecuteWorker(LogStats logic, int[] LocalCalls, double[] LocalTimes, int currentLogCount,
                                          uint currentIndex)
        {
            try
            {
                // todo
                // implement a custom sorting which also keeps track of the sum.
                // this will take this from
                // o(2*nlogn + n) to o(2*nlogn)

                Array.Sort(LocalCalls);
                Array.Sort(LocalTimes);


                for (var i = 0; i < Profiler.RECORDS_HELD; i++)
                {
                    logic.TotalCalls += LocalCalls[i];
                    logic.TotalTime  += LocalTimes[i];
                }

                // Mean
                logic.MeanTimePerCall         = logic.TotalTime / logic.TotalCalls;
                logic.MeanTimePerUpdateCycle  = logic.TotalTime / currentLogCount;
                logic.MeanCallsPerUpdateCycle = logic.TotalCalls / (float)currentLogCount;

                var medianOffset = Profiler.RECORDS_HELD - currentLogCount;
                var middle       = currentLogCount / 2;
                // Medians
                logic.MedianTime  = LocalTimes[medianOffset + middle];
                logic.MedianCalls = LocalCalls[medianOffset + middle];

                // Max
                logic.HighestTime  = LocalTimes[Profiler.RECORDS_HELD - 1];
                logic.HighestCalls = LocalCalls[Profiler.RECORDS_HELD - 1];

                // general
                logic.Entries = currentLogCount;

                lock (CurrentLogStats.sync
                      ) // Dump our current statistics into our static class which our drawing class uses
                {
                    CurrentLogStats.stats = logic;
                }
            }
            catch (Exception e)
            {
#if DEBUG
                ThreadSafeLogger.Error(
                    $"[Analyzer] Failed while calculating stats for profiler, errored with the message {e.Message}");
#else
                if (Settings.verboseLogging)
                {
                    ThreadSafeLogger.Error($"[Analyzer] Failed while calculating stats for profiler, errored with the message {e.Message}");
                }
#endif
            }
        }
示例#7
0
        public static void ProfilePatch()
        {
            MethodInfo    jiff = AccessTools.Method(typeof(StatExtension), nameof(StatExtension.GetStatValue));
            HarmonyMethod pre  = new HarmonyMethod(typeof(H_GetStatValue), nameof(Prefix));
            HarmonyMethod post = new HarmonyMethod(typeof(H_GetStatValue), nameof(Postfix));

            Modbase.Harmony.Patch(jiff, pre, post);


            jiff = AccessTools.Method(typeof(StatExtension), nameof(StatExtension.GetStatValueAbstract), new[] { typeof(BuildableDef), typeof(StatDef), typeof(ThingDef) });
            pre  = new HarmonyMethod(typeof(H_GetStatValue), nameof(PrefixAb));
            Modbase.Harmony.Patch(jiff, pre, post);

            jiff = AccessTools.Method(typeof(StatExtension), nameof(StatExtension.GetStatValueAbstract), new[] { typeof(AbilityDef), typeof(StatDef) });
            pre  = new HarmonyMethod(typeof(H_GetStatValue), nameof(PrefixAbility));
            Modbase.Harmony.Patch(jiff, pre, post);

            jiff = AccessTools.Method(typeof(StatWorker), nameof(StatWorker.GetValue), new[] { typeof(StatRequest), typeof(bool) });
            pre  = new HarmonyMethod(typeof(H_GetStatValue), nameof(GetValueDetour));
            Modbase.Harmony.Patch(jiff, pre);


            HarmonyMethod go   = new HarmonyMethod(typeof(H_GetStatValue), nameof(PartPrefix));
            HarmonyMethod biff = new HarmonyMethod(typeof(H_GetStatValue), nameof(PartPostfix));

            foreach (Type allLeafSubclass in typeof(StatPart).AllSubclassesNonAbstract())
            {
                try
                {
                    MethodInfo mef = AccessTools.Method(allLeafSubclass, nameof(StatPart.TransformValue), new Type[] { typeof(StatRequest), typeof(float).MakeByRefType() });
                    if (mef.DeclaringType == allLeafSubclass)
                    {
                        Patches info = Harmony.GetPatchInfo(mef);
                        bool    F    = true;
                        if (info != null)
                        {
                            foreach (Patch infoPrefix in info.Prefixes)
                            {
                                if (infoPrefix.PatchMethod == go.method)
                                {
                                    F = false;
                                }
                            }
                        }

                        if (F)
                        {
                            Modbase.Harmony.Patch(mef, go, biff);
                        }
                    }
                }
                catch (Exception)
                {
                    ThreadSafeLogger.Error($"Failed to patch {allLeafSubclass} from {allLeafSubclass.Assembly.FullName} for profiling");
                }
            }
        }
示例#8
0
 public static void ProfilePatch()
 {
     try
     {
         MethodTransplanting.PatchMethods(typeof(H_HarmonyPatches));
     }
     catch (Exception e)
     {
         ThreadSafeLogger.Error("Patching HarmonyPatches failed, errored with the message" + e.Message);
     }
 }
        private static void Error(string message)
        {
#if DEBUG
            ThreadSafeLogger.Error($"[Analyzer] Patching error: {message}");
#endif
#if NDEBUG
            if (!displayMessages)
            {
                return;
            }
            ThreadSafeLogger.Error($"[Analyzer] Patching error: {message}");
#endif
        }
示例#10
0
        public static void BeginUpdate()
        {
#if DEBUG
            if (Analyzer.CurrentlyPaused)
            {
                return;
            }

            if (midUpdate)
            {
                ThreadSafeLogger.Error("[Analyzer] Attempting to begin new update cycle when the previous update has not ended");
            }
            midUpdate = true;
#endif
        }
        // Mostly here for book keeping, optimised out of a release build.
        public static void BeginUpdate()
        {
            if (Analyzer.CurrentlyPaused)
            {
                return;
            }

            rootProf.Start();

            if (midUpdate)
            {
                ThreadSafeLogger.Error("[CRITICAL] Caught analyzer trying to begin a new update cycle before finishing the previous one.");
            }

            midUpdate = true;
        }
示例#12
0
        public void RecordMeasurement()
        {
#if DEBUG
            if (stopwatch.IsRunning)
            {
                ThreadSafeLogger.Error($"[Analyzer] Profile {key} was still running when recorded");
            }
#endif

            times[currentIndex] = stopwatch.Elapsed.TotalMilliseconds;
            hits[currentIndex]  = hitCounter;

            currentIndex = (currentIndex + 1) % RECORDS_HELD; // ring buffer

            stopwatch.Reset();
            hitCounter = 0;
        }
示例#13
0
        // Iterates through each child element in the document and attempts to extract method(s) from the strings inside the children
        private static void Parse(XmlDocument doc)
        {
            foreach (XmlNode node in doc.DocumentElement.ChildNodes) // entries should be
            {
                var meths = new HashSet <MethodInfo>();
                foreach (XmlNode child in node.ChildNodes)
                {
                    switch (child.Name.ToLower())
                    {
                    case "methods":
                    case "method":
                        foreach (XmlNode method in child.ChildNodes)
                        {
                            meths.Add(ParseMethod(method.InnerText));
                        }
                        break;

                    case "types":
                    case "type":
                        foreach (XmlNode type in child.ChildNodes)
                        {
                            meths.AddRange(ParseTypeMethods(type.InnerText));
                        }
                        break;

                    case "nestedtype":
                    case "nestedtypes":
                        foreach (XmlNode type in child.ChildNodes)
                        {
                            meths.AddRange(ParseSubTypeTypeMethods(type.InnerText));
                        }
                        break;

                    default:
                        ThreadSafeLogger.Error($"[Analyzer] Attempting to read unknown value from an Analyzer.xml, the given input was {child.Name}, it should have been either '(M/m)ethods', '(T/t)ypes' '(N/n)estedTypes");
                        break;
                    }
                }

                Type myType = DynamicTypeBuilder.CreateType(node.Name, meths);

                GUIController.Tab(Category.Modder).entries.Add(Entry.Create(myType.Name, Category.Modder, myType, false, true), myType);
            }
        }
        internal static void UpdateMethod(Type type, MethodInfo meth)
        {
            if (patchedMeths.Contains(meth))
            {
#if DEBUG
                ThreadSafeLogger.Warning($"[Analyzer] Already patched method {meth.DeclaringType.FullName + ":" + meth.Name}");
#else
                if (Settings.verboseLogging)
                {
                    ThreadSafeLogger.Warning($"[Analyzer] Already patched method {meth.DeclaringType.FullName + ":" + meth.Name}");
                }
#endif
                return;
            }

            patchedMeths.Add(meth);
            typeInfo.TryAdd(meth, type);

            Task.Factory.StartNew(delegate
            {
                try
                {
                    Modbase.Harmony.Patch(meth, transpiler: transpiler);
                }
                catch (Exception e)
                {
#if DEBUG
                    ThreadSafeLogger.Error($"[Analyzer] Failed to patch method {meth.DeclaringType.FullName + ":" + meth.Name} failed with the error {e.Message}");
#else
                    if (Settings.verboseLogging)
                    {
                        ThreadSafeLogger.Warning($"[Analyzer] Failed to patch method {meth.DeclaringType.FullName}:{meth.Name} failed with the error {e.Message}");
                    }
#endif
                }
            });
        }
示例#15
0
        public static void ExecutePatch()
        {
            try
            {
                if (patchType == Category.Tick)
                {
                    switch (input)
                    {
                    case CurrentInput.Method:
                        MethodTransplanting.UpdateMethods(typeof(CustomProfilersTick), Utility.GetMethods(currentInput));
                        break;

                    case CurrentInput.Type:
                        MethodTransplanting.UpdateMethods(typeof(CustomProfilersTick), Utility.GetTypeMethods(AccessTools.TypeByName(currentInput)));
                        break;

                    case CurrentInput.MethodHarmony:
                        MethodTransplanting.UpdateMethods(typeof(CustomProfilersTick), Utility.GetMethodsPatching(currentInput));
                        break;

                    case CurrentInput.SubClasses:
                        MethodTransplanting.UpdateMethods(typeof(CustomProfilersTick), Utility.SubClassImplementationsOf(AccessTools.TypeByName(currentInput), m => true));
                        break;

                    case CurrentInput.TypeHarmony:
                        MethodTransplanting.UpdateMethods(typeof(CustomProfilersTick), Utility.GetMethodsPatchingType(AccessTools.TypeByName(currentInput)));
                        break;

                    case CurrentInput.InternalMethod:
                        Utility.PatchInternalMethod(currentInput, Category.Tick);
                        return;

                    case CurrentInput.Assembly:
                        Utility.PatchAssembly(currentInput, Category.Tick);
                        return;
                    }

                    GUIController.SwapToEntry("Custom Tick");
                }
                else
                {
                    switch (input)
                    {
                    case CurrentInput.Method:
                        MethodTransplanting.UpdateMethods(typeof(CustomProfilersUpdate), Utility.GetMethods(currentInput));
                        break;

                    case CurrentInput.Type:
                        MethodTransplanting.UpdateMethods(typeof(CustomProfilersUpdate), Utility.GetTypeMethods(AccessTools.TypeByName(currentInput)));
                        break;

                    case CurrentInput.MethodHarmony:
                        MethodTransplanting.UpdateMethods(typeof(CustomProfilersUpdate), Utility.GetMethodsPatching(currentInput));
                        break;

                    case CurrentInput.SubClasses:
                        MethodTransplanting.UpdateMethods(typeof(CustomProfilersUpdate), Utility.SubClassImplementationsOf(AccessTools.TypeByName(currentInput), m => true));
                        break;

                    case CurrentInput.TypeHarmony:
                        MethodTransplanting.UpdateMethods(typeof(CustomProfilersUpdate), Utility.GetMethodsPatchingType(AccessTools.TypeByName(currentInput)));
                        break;

                    case CurrentInput.InternalMethod:
                        Utility.PatchInternalMethod(currentInput, Category.Update);
                        return;

                    case CurrentInput.Assembly:
                        Utility.PatchAssembly(currentInput, Category.Update);
                        return;
                    }

                    GUIController.SwapToEntry("Custom Update");
                }
            }
            catch (Exception e)
            {
                ThreadSafeLogger.Error($"Failed to process input, failed with the error {e.Message}");
            }
        }