我发现无法在.NET中的所有线程上转储堆栈。既不发送信号到进程也不编程访问所有线程。我只能通过Thread.CurrentThread访问当前线程。
任何技巧?
答案 0 :(得分:11)
如果您正在尝试在进程已经运行时获取堆栈转储(la jstack),则有两种方法如here所述:
有一个鲜为人知但有效的工具叫Managed Stack Explorer。虽然它具有基本的GUI,但如果你添加到路径,它可以有效地成为jstack的.NET等价物;那么这只是一个打字问题:
mse /s /p <pid>
我发现有必要采取生产线程转储,这对我有用。希望它有所帮助: - )
答案 1 :(得分:6)
为了节省其他人的麻烦,这里的上述端口为c#:
static void WriteThreadInfo(StringBuilder sw, IEnumerable<Thread> threads)
{
foreach(Thread thread in threads)
{
if(!thread.IsAlive) continue;
sw.Append(String.Concat("THREAD NAME: ", thread.Name));
sw.Append(GetStackTrace(thread));
sw.AppendLine();
sw.AppendLine();
}
}
static String GetStackTrace(Thread t)
{
t.Suspend();
var trace1 = new StackTrace(t, true);
t.Resume();
String text1 = System.Environment.NewLine;
var builder1 = new StringBuilder(255);
for (Int32 num1 = 0; (num1 < trace1.FrameCount); num1++)
{
StackFrame frame1 = trace1.GetFrame(num1);
builder1.Append(" at ");
System.Reflection.MethodBase base1 = frame1.GetMethod();
Type type1 = base1.DeclaringType;
if (type1 != null)
{
String text2 = type1.Namespace;
if (text2 != null)
{
builder1.Append(text2);
builder1.Append(".");
}
builder1.Append(type1.Name);
builder1.Append(".");
}
builder1.Append(base1.Name);
builder1.Append("(");
System.Reflection.ParameterInfo [] infoArray1 = base1.GetParameters();
for (Int32 num2 = 0; (num2 < infoArray1.Length); num2++)
{
String text3 = "<UnknownType>";
if (infoArray1[num2].ParameterType != null)
{
text3 = infoArray1[num2].ParameterType.Name;
}
builder1.Append(String.Concat(((num2 != 0) ? ", " : ""), text3, " ", infoArray1[num2].Name));
}
builder1.Append(")");
if (frame1.GetILOffset() != -1)
{
String text4 = null;
try
{
text4 = frame1.GetFileName();
}
catch (System.Security.SecurityException)
{
}
if (text4 != null)
{
builder1.Append(String.Concat(" in ", text4, ":line ", frame1.GetFileLineNumber().ToString()));
}
}
if (num1 != (trace1.FrameCount - 1))
{
builder1.Append(text1);
}
}
return builder1.ToString();
}
我没有找到一种方法来获取C#中所有托管线程的列表(只有ProcessThreads),所以看起来你需要维护自己感兴趣的线程列表。
此外,我发现我无法在正在运行的线程上调用新的Stacktrace(t,true),因此添加了暂停和恢复。 显然,你需要考虑这是否会导致问题,如果您将线程转储生产应用。
不过,我们已经将这个电话放在我们的应用程序wcf rest界面上,所以很容易做到。答案 2 :(得分:2)
此时我看到为.NET CLR生成线程转储的最佳工具是DebugDiag。此工具将生成活动CLR线程的非常详细的报告(使用Crash / Hang分析器)以及建议。
我建议查看以下.NET DebugDiag tutorial,因为它显示了生产问题后的分析过程。步骤如下:
答案 3 :(得分:1)
我为过去曾参与的项目写了一个翻斗车:
void CrashHandler::WriteThreadInfo(StringWriter* sw, ArrayList* threads, String* type)
{
sw->WriteLine(type);
IEnumerator* ie = threads->GetEnumerator();
while(ie->MoveNext())
{
botNETThread* bnt = static_cast<botNETThread*>(ie->Current);
if(!bnt->IsAlive) continue;
sw->WriteLine(String::Concat(S"ORIGIN ASSEMBLY: ", bnt->Assembly->FullName));
sw->WriteLine(String::Concat(S"THREAD NAME: ", (bnt->Name && bnt->Name->Length)?bnt->Name:S"Unnamed thread"));
sw->Write(GetStackTrace(bnt->_thread));
sw->WriteLine();
sw->WriteLine();
}
}
String* CrashHandler::GetStackTrace(Thread* t)
{
System::Diagnostics::StackTrace __gc * trace1 = __gc new System::Diagnostics::StackTrace(t, true);
System::String __gc * text1 = System::Environment::NewLine;
System::Text::StringBuilder __gc * builder1 = __gc new System::Text::StringBuilder(255);
for (System::Int32 num1 = 0; (num1 < trace1->FrameCount); num1++)
{
System::Diagnostics::StackFrame __gc * frame1 = trace1->GetFrame(num1);
builder1->Append(S" at ");
System::Reflection::MethodBase __gc * base1 = frame1->GetMethod();
System::Type __gc * type1 = base1->DeclaringType;
if (type1 != 0)
{
System::String __gc * text2 = type1->Namespace;
if (text2 != 0)
{
builder1->Append(text2);
if (builder1 != 0)
{
builder1->Append(S".");
}
}
builder1->Append(type1->Name);
builder1->Append(S".");
}
builder1->Append(base1->Name);
builder1->Append(S"(");
System::Reflection::ParameterInfo __gc * infoArray1 __gc [] = base1->GetParameters();
for (System::Int32 num2 = 0; (num2 < infoArray1->Length); num2++)
{
System::String __gc * text3 = S"<UnknownType>";
if (infoArray1[num2]->ParameterType != 0)
{
text3 = infoArray1[num2]->ParameterType->Name;
}
builder1->Append(System::String::Concat(((num2 != 0) ? S", " : S""), text3, S" ", infoArray1[num2]->Name));
}
builder1->Append(S")");
if (frame1->GetILOffset() != -1)
{
System::String __gc * text4 = 0;
try
{
text4 = frame1->GetFileName();
}
catch (System::Security::SecurityException*)
{
}
if (text4 != 0)
{
builder1->Append(System::String::Concat(S" in ", text4, S":line ", frame1->GetFileLineNumber().ToString()));
}
}
if (num1 != (trace1->FrameCount - 1))
{
builder1->Append(text1);
}
}
return builder1->ToString();
}
您可以使用Process.GetCurrentProcess()。线程来获取线程
而且我知道我使用托管C ++,但它很容易遵循。我采取线程的arraylist,因为为了我的目的,我已经将我的线程catagorized。是的,我使用以前编写的堆栈帧代码,因为我当时是MC ++的新手:)
整个文件为here。这是我前段时间写的Diablo II botting engine。
答案 4 :(得分:1)
如果您需要以编程方式执行此操作(可能您希望在CI过程中自动转储),则可以使用this answer中的信息来解决其他问题。
基本上,使用CLR MD附加到您自己的流程:
using Microsoft.Diagnostics.Runtime;
using (DataTarget target = DataTarget.AttachToProcess(
Process.GetCurrentProcess().Id, 5000, AttachFlag.Passive))
{
ClrRuntime runtime = target.ClrVersions.First().CreateRuntime();
foreach (ClrThread thread in runtime.Threads)
{
IList<ClrStackFrame> stackFrames = thread.StackTrace;
PrintStackTrace(stackFrames);
}
}
这里PrintStackTrace留给读者练习。
答案 5 :(得分:0)