我们的客户在使用.NET编写的应用程序时遇到问题。它在流程开始后消耗整个CPU,而不显示任何窗口。它看起来像一个无限循环。
问题是,该客户拥有Windows 7 Home Edition,这就是我无法将远程托管调试器连接到应用程序的原因。
我可以用一些调试模式创建特殊版本。我认为我可以在进程的乞讨中运行一个线程,大约一分钟后调用Debugger.Break并以某种方式记录线程和堆栈帧。我不确定是否可以在所有线程上记录堆栈帧。
你是否有一些没有调试器的调试经验?
更新:
经过两天的碾压,我终于找到了问题。这是一个糟糕的ATI显卡驱动程序,它导致无限循环。因为一些组件使用OpenGL。通过迭代当前进程的线程,暂停它们写入堆栈跟踪,很容易发现问题。我最终用来揭示错误的代码如下:
using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
public class DebuggerContext
{
public int TimeIntervalMs { get; set; }
public Thread InfiniteLoopThread { get; set; }
}
public static class InfiniteLogger
{
public static void LogInfniteLoopAfterTimeInterval(int timeIntervalMs)
{
Thread watchdog = new Thread(Watchdog);
DebuggerContext context = new DebuggerContext()
{
InfiniteLoopThread = Thread.CurrentThread,
TimeIntervalMs = timeIntervalMs,
};
watchdog.Start(context);
}
static void Watchdog(object threadContext)
{
DebuggerContext context = (DebuggerContext)threadContext;
Thread.Sleep(context.TimeIntervalMs);
LogThread(context.InfiniteLoopThread);
var threads = Process.GetCurrentProcess().Threads;
foreach (var thread in threads)
{
if (thread == Thread.CurrentThread)
continue;
LogThread(context.InfiniteLoopThread);
}
}
private static void LogThread(Thread thread)
{
thread.Suspend();
StackTrace stackTrace = new StackTrace(thread, true);
string log = "The infinite loop is in: " + StackTraceToString(stackTrace);
Trace.WriteLine(log);
thread.Resume();
}
/// <summary>
/// http://stackoverflow.com/questions/51768/print-stack-trace-information-from-c-sharp
/// </summary>
/// <returns></returns>
static string StackTraceToString(StackTrace stackTrace)
{
StringBuilder sb = new StringBuilder(256);
var frames = stackTrace.GetFrames();
for (int i = 0; i < frames.Length; i++) /* Ignore current StackTraceToString method...*/
{
var currFrame = frames[i];
var method = currFrame.GetMethod();
var line = currFrame.GetFileLineNumber();
var file = currFrame.GetFileName();
var column = currFrame.GetFileColumnNumber();
sb.AppendLine(string.Format("{0}:[{1},{2}], {3}.{4}",
file,
line,
column,
method.ReflectedType != null ? method.ReflectedType.Name : string.Empty,
method.Name
));
}
return sb.ToString();
}
}
[STAThread]
static void Main(string[] args)
{
InfiniteLogger.LogInfniteLoopAfterTimeInterval(30 * 1000); //log thread state after 30 seconds after start
....
}
答案 0 :(得分:4)
如果您有所有正在运行的线程的列表,则可以打印堆栈帧,但不推荐。
StackTrace
有一个过时的构造函数 - StackTrace Constructor (Thread, Boolean)。
所以你可以这样做:
foreach (var thread in list)
{
thread.Suspend();
Console.WriteLine(thread.ManagedThreadId);
System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(thread, true);
Console.WriteLine(trace);
}
请阅读MSDN中的备注部分,因为当您暂停某个线程时,可能会导致死锁......
重要提示
不要使用此构造函数。它已经过时了, 并且没有推荐的替代方案。暂停线程时 你无法知道它正在执行什么代码,以及死锁 很容易发生。例如,如果你暂停一个线程 在安全权限评估期间持有锁,其他线程在 AppDomain可能被阻止。如果你暂停一个线程 执行一个类构造函数,AppDomain中的其他线程 尝试使用该类被阻止。
我同意其他人的意见,你最好采用日志方式,而不是试图暂停和打印堆栈..
答案 1 :(得分:1)
在我看来,最好开始构建一个日志记录结构,在Windows日志(或者可能是文件)中写入程序在某个时刻执行的指令。 您可以将其作为一个选项,这样您就可以生成可执行文件,并在您的每个客户端上使用此类日志记录。
无论如何,如果你有兴趣记录所有线程的堆栈跟踪,这个答案可能对你有用Print thread stack of all threads of a process
答案 2 :(得分:1)
感谢您的提示。该应用程序拥有自己的日志记录系统,涵盖了99%的情况,但您无法记录所有内容。最后我设法做了简单的应用来证明无限循环记录确实有效:
using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
namespace InfiniteLoopDebug
{
public class DebuggerContext
{
public int TimeIntervalMs { get; set; }
public Thread InfiniteLoopThread { get; set; }
}
class Program
{
static void Main(string[] args)
{
LogInfniteLoopAfterTimeInterval(1200);
WorkerMethod();
}
static void WorkerMethod()
{
int a = 0;
int b = 1;
while (true) //infinite loop
{
int c = a + b;
b = a;
}
}
static void LogInfniteLoopAfterTimeInterval(int timeIntervalMs)
{
Thread watchdog = new Thread(Watchdog);
DebuggerContext context = new DebuggerContext()
{
InfiniteLoopThread = Thread.CurrentThread,
TimeIntervalMs = timeIntervalMs,
};
watchdog.Start(context);
}
static void Watchdog(object threadContext)
{
DebuggerContext context = (DebuggerContext)threadContext;
Thread.Sleep(context.TimeIntervalMs);
context.InfiniteLoopThread.Suspend();
//Thread.Sleep(100);
StackTrace stackTrace = new StackTrace(context.InfiniteLoopThread, true);
string log = "The infinite loop is in: " +StackTraceToString(stackTrace);
Trace.WriteLine( log);
Console.WriteLine(log);
context.InfiniteLoopThread.Resume();
}
/// <summary>
/// http://stackoverflow.com/questions/51768/print-stack-trace-information-from-c-sharp
/// </summary>
/// <returns></returns>
static public string StackTraceToString(StackTrace stackTrace)
{
StringBuilder sb = new StringBuilder(256);
var frames = stackTrace.GetFrames();
for (int i = 0; i < frames.Length; i++) /* Ignore current StackTraceToString method...*/
{
var currFrame = frames[i];
var method = currFrame.GetMethod();
var line = currFrame.GetFileLineNumber();
var file = currFrame.GetFileName();
var column = currFrame.GetFileColumnNumber();
sb.AppendLine(string.Format("{0}:[{1},{2}], {3}.{4}",
file,
line,
column,
method.ReflectedType != null ? method.ReflectedType.Name : string.Empty,
method.Name
));
}
return sb.ToString();
}
}
}
答案 3 :(得分:0)
虽然这样做可能是可行的,但我认为更好的方法是将日志记录添加到应用程序中。始终将日志记录语句添加到应用程序,并采用可配置的方式在需要时启用日志记录。
使用现有的日志记录框架(例如Log4Net) - 他们可以选择在不同的步骤中启用日志记录。