如何调试挂起动作

时间:2016-07-18 15:01:15

标签: asp.net-mvc iis

我们有一个MVC应用程序,每个人都知道然后(从一天几次到每2-3天一次的任何事情)遭受站点范围的挂起(它不会响应任何请求而不会重新启动它拥有)。我们设法将嫌疑人的数量减少到一个页面(每当网站遭受挂起时,此页面将显示在挂起请求的顶部)。知道哪个页面是罪魁祸首并没有帮助,因为我们无法在开发机器上重新创建问题。事实上,我们甚至无法在生产机器上重新创建问题(大多数情况下,只需打开有问题的页面就不会破坏任何东西)。

我们所知道的是,有时当用户访问此页面(控制器操作)时,该站点将挂起,我们知道发出请求的方式并不多,这可能会导致挂起(来自IIS)日志我们知道用户如何到达有缺陷的页面,并且我们正在处理一个没有查询字符串参数的简单GET。)

我们想知道挂起发生在代码中的确切位置,但我们不知道如何从app / worker线程/ IIS / Windows Server中获取此类信息。没有异常保存到Windows日志中,我们内置到应用程序记录器中也没有选择任何异常(可能与挂起有关)。

有什么方法可以告诉IIS工作线程到底在做什么(比如获取相应的文件和代码行)?

P.S。我在另一个问题中描述了网站范围内的确切症状,但这些问题与此问题无关。此外,问题最终过于宽泛,只带来了一般性答案。 P.S.2 我们通过查看IIS中的工作进程\请求找到了违规页面。

1 个答案:

答案 0 :(得分:3)

在过去,我遇到了同样的问题,真正快速帮助我的是能够使用Mono.Cecil在DLL中注入一行跟踪代码。下面的代码将跟踪注入到DLL的无符号版本中,并输出一个新的DLL,可以通过在开始时注入日志行来在代码中创建“缩进的跟踪”(如果需要则结束......下面未显示)每种方法的方法,以便您可以计时和每次通话的存在。有很多第三方工具,但这很容易(最好是一天工作),给开发人员完全控制,作为奖励,是免费的。

您还需要创建一个DLL(下面的IISStackTraceProvider),它具有跟踪类和静态调用“TraceStep”,可用于记录数据。要跨工作进程和线程重建该数据,可以使用“HttpContext.Items属性”将GUID和秒表与每个BeginRequest / EndRequest相关联。由于您的呼叫挂起...您想要跟踪任何“开始”但从不“结束”的呼叫或结束时间,并将其余部分抛弃以保持快速。

我已经在我们的生产环境中的Web场中每小时/每个服务器进行了大约一百万次调用,而不会影响性能,但要注意要记录的请求以及丢弃哪些请求。此外,我使用Redis来捕获日志,因为写入时间非常快并且它也是免费的,然后在我捕获问题后才读取Redis数据。

class TraceInjection
{
    private ELogLevel logLevel;
    public enum ELogLevel
    {
        eLow,
        eMid,
        eHigh
    }

    public TraceInjection(ELogLevel logLevel)
    {
        this.logLevel = logLevel;
    }

    public bool InjectTracingLine(string assemblyPath, string outputDirectory)
    {
        CustomAttribute customAttr;
        AssemblyDefinition asmDef;

        // New assembly path
        string fileName = Path.GetFileName(assemblyPath);

        string newPath = outputDirectory + "\\" + fileName;

        // Check if Output directory already exists, if not, create one
        if (!Directory.Exists(outputDirectory))
        {
            Directory.CreateDirectory(outputDirectory);
        }

        ModuleDefinition modDefCopy = null;
        TypeDefinition typDefCopy = null;

        try
        {
            var resolver = new DefaultAssemblyResolver();
            resolver.AddSearchDirectory(System.IO.Path.GetDirectoryName(assemblyPath));

            var parameters = new ReaderParameters
            {
                AssemblyResolver = resolver,
            };

            // Load assembly
            asmDef = AssemblyDefinition.ReadAssembly(assemblyPath, parameters);

            String functionsFound = "";

            foreach (var modDef in asmDef.Modules)
            {
                modDefCopy = modDef;
                foreach (var typDef in modDef.Types)
                {
                    typDefCopy = typDef;                        
                    foreach (MethodDefinition metDef in typDef.Methods)
                    {
                        try
                        {
                            // Skipping things I personally don't want traced...
                            if (metDef.IsConstructor || 
                                metDef.IsAbstract ||
                                metDef.IsCompilerControlled ||
                                metDef.IsGetter ||
                                metDef.IsSetter
                                ) continue;

                            functionsFound += String.Format("{0}\r\n", metDef.Name.Trim());


                            // Get ILProcessor
                            ILProcessor ilProcessor = metDef.Body.GetILProcessor();

                            /*** Begin Method ******/
                            // Load fully qualified method name as string
                            Instruction i1 = ilProcessor.Create(
                                OpCodes.Ldstr,
                                String.Format(">,{0},{1}", metDef.Name.Replace(",", ""), asmDef.Name.Name)
                            );
                            ilProcessor.InsertBefore(metDef.Body.Instructions[0], i1);

                            // Call the method which would write tracing info
                            Instruction i2 = ilProcessor.Create(
                                OpCodes.Call,
                                metDef.Module.Import(
                                    typeof(IISStackTraceProvider).GetMethod("TraceStep", new[] { typeof(string) })
                                )
                            );
                            ilProcessor.InsertAfter(i1, i2);

                        }catch(Exception ex)
                        {
                            // ...
                        }
                    }
                }
            }

            Console.Write(functionsFound);
            Console.ReadKey();

            // Save modified assembly
            asmDef.Write(newPath, new WriterParameters() { WriteSymbols = true });
        }
        catch (Exception ex)
        {
            modDefCopy = null;
            typDefCopy = null;

            // Nothing to be done, just let the caller handle exception 
            // or do logging and so on
            throw;
        }

        return true;
    }

    public bool TryGetCustomAttribute(MethodDefinition type, string attributeType, out CustomAttribute result)
    {
        result = null;
        if (!type.HasCustomAttributes)
            return false;

        foreach (CustomAttribute attribute in type.CustomAttributes)
        {
            if (attribute.Constructor.DeclaringType.FullName != attributeType)
                continue;

            result = attribute;
            return true;
        }

        return false;
    }
}