You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#/Xamarin/Android中如何获取所有托管线程的堆栈跟踪?

Xamarin.Android ANR排查:托管线程全量堆栈抓取方案

问题核心原因

Process.GetCurrentProcess().Threads在Xamarin.Android的Mono运行时下未做托管层适配,返回的是内核原生线程的非托管封装对象,没有和托管线程上下文做映射,因此遍历后拿到的条目全为null,无法直接用于托管栈抓取。
现有面向.NET Framework/.NET Core桌面、服务端的线程栈抓取方案,大多依赖Windows平台的调试API或是.NET Core的运行时事件源,在Mono运行时的Android环境下无法直接运行。

可落地实现方案

Xamarin.Android基于Mono运行时,可通过反射调用Mono内部接口实现全托管线程的堆栈抓取,无需引入第三方组件,Release环境下也可正常运行:

  • 前置配置
    打包前在项目属性中把调试符号设置为Portable PDB并选择嵌入程序集,否则抓取到的堆栈会缺失方法名、行号信息,无法定位问题。
  • 核心实现代码
using System.Reflection;
using System.Text;

public static string DumpAllManagedThreadStacks()
{
    var logBuilder = new StringBuilder();
    var monoRuntimeType = Type.GetType("Mono.Runtime");
    var getStackMethod = monoRuntimeType?.GetMethod("GetStackTrace",
        BindingFlags.Static | BindingFlags.NonPublic,
        null,
        new Type[] { typeof(Thread), typeof(bool) },
        null);

    if (getStackMethod == null)
    {
        logBuilder.AppendLine("当前Mono版本不支持托管栈抓取");
        return logBuilder.ToString();
    }

    foreach (var thread in GetAliveManagedThreads())
    {
        try
        {
            logBuilder.AppendLine($"=== ThreadId:{thread.ManagedThreadId} Name:{thread.Name ?? "unnamed"} State:{thread.ThreadState} ===");
            var stackContent = getStackMethod.Invoke(null, new object[] { thread, true }) as string;
            logBuilder.AppendLine(stackContent);
            logBuilder.AppendLine();
        }
        catch (Exception ex)
        {
            logBuilder.AppendLine($"Dump thread {thread.ManagedThreadId} failed: {ex.Message}");
        }
    }
    return logBuilder.ToString();
}

private static List<Thread> GetAliveManagedThreads()
{
    var threads = new List<Thread>();
    var threadType = typeof(Thread);
    var threadHashField = threadType.GetField("thread_hash", BindingFlags.Static | BindingFlags.NonPublic);
    if (threadHashField?.GetValue(null) is not IEnumerable threadHash) return threads;
    
    foreach (var entry in threadHash)
    {
        var valueField = entry.GetType().GetField("value", BindingFlags.Instance | BindingFlags.NonPublic);
        if (valueField?.GetValue(entry) is Thread thread)
        {
            threads.Add(thread);
        }
    }
    return threads;
}
  • 注意事项
    • 上述反射逻辑适配Mono 6.0及以上版本,覆盖2019年之后发布的所有Xamarin.Android正式版本
    • 栈抓取逻辑必须放在ANR看门狗的独立工作线程执行,不能依赖已经阻塞的主线程调度,否则ANR触发时逻辑无法正常运行
    • 抓取到的堆栈直接写入应用私有目录的独立日志文件,不要通过logcat输出,规避故障时段logcat丢日志的问题

扩展定位能力

如果需要同时覆盖Java层、Native层阻塞导致的ANR,可在托管栈抓取完成后调用Debug.NativeStackTraces()获取全量原生线程堆栈,通过线程ID和托管栈做映射,即可覆盖全链路的阻塞定位需求。


内容的提问来源于stack exchange,提问作者Ken K

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 20:15:52