【问题标题】:Xamarin Android native call stackXamarin Android 本机调用堆栈
【发布时间】:2018-05-18 19:17:59
【问题描述】:

我是新的 Xamarin 开发人员,我想为 Xamarin 开发准备诊断工具。在我的情况下,我想从 C# 异常中获取托管堆栈跟踪,并从其余库(如果可能是 C++,Java)中获取非托管堆栈跟踪。我尝试调用 C++ 方法,使用一些奇怪的选项初始化控件,调用 java 库(谷歌地图),但我仍然没有带有本机调用堆栈的堆栈跟踪。

您能帮我解释一下如何从 Xamarin Android 应用程序获取带有托管和非托管堆栈帧的堆栈跟踪吗?

非常感谢!

【问题讨论】:

  • 您是否在寻找包含 Java 异常或 NDK/JNI 异常的异常/堆栈跟踪?
  • 是的。我总是只从托管代码中获取带有堆栈帧的 C# 堆栈跟踪

标签: java c# xamarin xamarin.android monodevelop


【解决方案1】:

来自其他库(C++、java)的非托管堆栈跟踪

就 C 而言,没有异常处理,C++ 确实有异常,但它们与 CIL 管理的异常完全不同。现在,您可以在 NDK/C++ 代码中实现 C++ 异常,如果可能的话,可以基于异常和继续运行的能力,抑制异常,并通过 Mono 运行时创建托管异常,该异常以某种方式表示 C++ 异常。有关运行时异常传播的更多信息,请参阅 Mono 文档。就我个人而言,我只是在 C++ 级别处理 C++ 异常、记录它们、报告它们等等......并且不尝试将异常传播回托管代码(付出太多开销而回报太少)。

就各种Xamarin.Android/Android 异常而言:

Mono 运行时/NDK 崩溃

考虑类似 libc 的字符串 strncpy:

[DllImport("libc.so")]
private static extern void strncpy(StringBuilder dest, string src, uint n);

并用空值(或 IntPtr.Zero)调用它:

strncpy(null, null, 1);

一个令人讨厌的运行时崩溃:

[mono-rt] Stacktrace:
[mono-rt] 
[mono-rt]   at <unknown> <0xffffffff>
[mono-rt]   at (wrapper managed-to-native) Android_Clipboard.MainActivity.strncpy (System.Text.StringBuilder,string,uint) <0x00012>
[mono-rt]   at Android_Clipboard.MainActivity/<>c__DisplayClass5_0.<OnCreate>b__0 (object,System.EventArgs) [0x0004f] in /Volumes/Code/code/Projects/Android_Clipboard/Android_Clipboard/MainActivity.cs:57
[mono-rt]   at Android.Views.View/IOnClickListenerImplementor.OnClick (Android.Views.View) [0x00011] in <25661073a35344a89f215a4cf81af37c>:0
[mono-rt]   at Android.Views.View/IOnClickListenerInvoker.n_OnClick_Landroid_view_View_ (intptr,intptr,intptr) [0x00011] in <25661073a35344a89f215a4cf81af37c>:0
[mono-rt]   at (wrapper dynamic-method) object.cea8479a-a63b-41ac-a9f6-b0fe3b09df49 (intptr,intptr,intptr) [0x00017] in <f32579baafc1404fa37ba3ec1abdc0bd>:0
[mono-rt]   at (wrapper native-to-managed) object.cea8479a-a63b-41ac-a9f6-b0fe3b09df49 (intptr,intptr,intptr) [0x00022] in <f32579baafc1404fa37ba3ec1abdc0bd>:0
[mono-rt] /proc/self/maps:
[mono-rt] 12c00000-42c00000 rw-p 00000000 00:01 207683     /dev/ashmem/dalvik-main space (region space) (deleted)
[mono-rt] 70dca000-70f78000 rw-p 00000000 fc:00 106531     /data/dalvik-cache/x86/system@framework@boot.art
[mono-rt] 70f78000-71014000 rw-p 00000000 fc:00 106532     
~~~
[mono-rt] No native Android stacktrace (see debuggerd output).
[mono-rt] 
[mono-rt] 
[mono-rt] =================================================================
[mono-rt] Got a SIGSEGV while executing native code. This usually indicates
[mono-rt] a fatal error in the mono runtime or one of the native libraries 
[mono-rt] used by your application.
[mono-rt] =================================================================
[mono-rt] 
[libc] Fatal signal 11 (SIGSEGV), code 1, fault addr 0x0 in tid 14916 (droid_Clipboard), pid 14916 (droid_Clipboard)

Mono/CIL 托管异常

从 CIL Mono VM 调用到 Java 的任何内容都会通过 VM2VM 桥 (CilVMMono/JNIJavaVM) 由 Mono JNIEnv 处理,因此由 Mono 运行时包装在托管异常中。

考虑反射一个不存在的 Java 字段(来自 C#):

var someWidget = FindViewById<Button>(Resource.Id.myButton);
Java.Lang.Reflect.Field f = someWidget.Class.GetField("SomethingThatDoesNotExist");

产生一个基于 Java 的 java.lang.NoSuchFieldException,它被包装并呈现为托管异常:

MonoDroid] UNHANDLED EXCEPTION:
[MonoDroid] Java.Lang.NoSuchFieldException: SomethingThatDoesNotExist
[MonoDroid]   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <f32579baafc1404fa37ba3ec1abdc0bd>:0 
[MonoDroid]   at Java.Interop.JniEnvironment+InstanceMethods.CallObjectMethod (Java.Interop.JniObjectReference instance, Java.Interop.JniMethodInfo method, Java.Interop.JniArgumentValue* args) [0x00069] in <09bf3e262b934ffab2ba01f9fc7fd54d>:0 
[MonoDroid]   at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeAbstractObjectMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) [0x00014] in <09bf3e262b934ffab2ba01f9fc7fd54d>:0 
[MonoDroid]   at Java.Lang.Class.GetField (System.String name) [0x00029] in <25661073a35344a89f215a4cf81af37c>:0 
[MonoDroid]   at Android_Clipboard.MainActivity+<>c__DisplayClass5_0.<OnCreate>b__0 (System.Object <p0>, System.EventArgs <p1>) [0x00048] in /Volumes/Code/code/Projects/Android_Clipboard/Android_Clipboard/MainActivity.cs:53 
[MonoDroid]   at Android.Views.View+IOnClickListenerImplementor.OnClick (Android.Views.View v) [0x00011] in <25661073a35344a89f215a4cf81af37c>:0 
[MonoDroid]   at Android.Views.View+IOnClickListenerInvoker.n_OnClick_Landroid_view_View_ (System.IntPtr jnienv, System.IntPtr native__this, System.IntPtr native_v) [0x0000f] in <25661073a35344a89f215a4cf81af37c>:0 
[MonoDroid]   at (wrapper dynamic-method) System.Object.7ad62bce-4e52-4d67-9099-969546fceb57(intptr,intptr,intptr)
[MonoDroid]   --- End of managed Java.Lang.NoSuchFieldException stack trace ---
[MonoDroid] java.lang.NoSuchFieldException: SomethingThatDoesNotExist
[MonoDroid]     at java.lang.Class.getField(Class.java:1601)
[MonoDroid]     at mono.android.view.View_OnClickListenerImplementor.n_onClick(Native Method)
[MonoDroid]     at mono.android.view.View_OnClickListenerImplementor.onClick(View_OnClickListenerImplementor.java:30)
[MonoDroid]     at android.view.View.performClick(View.java:6294)
[MonoDroid]     at android.view.View$PerformClick.run(View.java:24770)
[MonoDroid]     at android.os.Handler.handleCallback(Handler.java:790)
[MonoDroid]     at android.os.Handler.dispatchMessage(Handler.java:99)
[MonoDroid]     at android.os.Looper.loop(Looper.java:164)
[MonoDroid]     at android.app.ActivityThread.main(ActivityThread.java:6494)
[MonoDroid]     at java.lang.reflect.Method.invoke(Native Method)
[MonoDroid]     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
[MonoDroid]     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
[MonoDroid] 

“本机”Java 异常

考虑在 Mono/CIL 调用的上下文中未发生的 Java 异常:

Java:

private static void stackOverflowLoop() {
    CrashMe.stackOverflowLoop();
}
public void stackOverflow() {
    new Handler().postDelayed(new Runnable() {
        public void run() {
            CrashMe.stackOverflowLoop();
        }
    }, 2000);
}

C#:

new CrashMe().StackOverflow();

产生在 Android 运行时中捕获的“纯”Java 异常(请记住,Xamarin.Android 应用程序只是基于 NDK 的 C 应用程序,具有 CIL 虚拟机并可以访问 Java 虚拟机)

AndroidRuntime] Shutting down VM
[AndroidRuntime] FATAL EXCEPTION: main
[AndroidRuntime] Process: com.sushihangover.Android_Clipboard, PID: 23664
[AndroidRuntime] java.lang.StackOverflowError: stack size 8MB
[AndroidRuntime]    at com.sushihangover.mylibrary.CrashMe.stackOverflowLoop(CrashMe.java:7)
~~~~
[AndroidRuntime]    at com.sushihangover.mylibrary.CrashMe.stackOverflowLoop(CrashMe.java:7)
[AndroidRuntime]    at com.sushihangover.mylibrary.CrashMe.access$000(CrashMe.java:5)
[AndroidRuntime]    at com.sushihangover.mylibrary.CrashMe$1.run(CrashMe.java:12)
[AndroidRuntime]    at android.os.Handler.handleCallback(Handler.java:790)
[AndroidRuntime]    at android.os.Handler.dispatchMessage(Handler.java:99)
[AndroidRuntime]    at android.os.Looper.loop(Looper.java:164)
[AndroidRuntime]    at android.app.ActivityThread.main(ActivityThread.java:6494)
[AndroidRuntime]    at java.lang.reflect.Method.invoke(Native Method)
[AndroidRuntime]    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
[AndroidRuntime]    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

基于 Java 的 Android 无响应 (ANR)

仅被 Android 操作系统捕获/捕获。

Java:

public void androidNotReponding() {
    new Handler().postDelayed(new Runnable() {
        public void run() {
            while (true) {
            }
        }
    }, 2000);

C#

new CrashMe().AndroidNotReponding();

ANR 日志猫:

[zygote] Thread[3,tid=25575,WaitingInMainSignalCatcherLoop,Thread*=0xb224ec00,peer=0x13880020,"Signal Catcher"]: reacting to signal 3
[zygote] 
[zygote] Wrote stack traces to '[tombstoned]'
E ActivityManager: ANR in com.sushihangover.Android_Clipboard (com.sushihangover.Android_Clipboard/md58bbdf49b937b7d185e88b8dfe7516c89.MainActivity)
E ActivityManager: PID: 25886
E ActivityManager: Reason: Input dispatching timed out (Waiting to send non-key event because the touched window has not finished processing certain input events that were delivered to it over 500.0ms ago. 

ANR 神石痕迹:

Cmd line: com.sushihangover.Android_Clipboard
~~~~
Total blocking GC count: 0
Total blocking GC time: 0
~~~~
suspend all histogram:  Sum: 576us 99% C.I. 2us-329.200us Avg: 30.315us Max: 346us
DALVIK THREADS (13):
"main" prio=5 tid=1 Runnable
  | group="main" sCount=0 dsCount=0 obj=0x73997258 self=0xb4034500
  | sysTid=4484 nice=0 cgrp=default sched=0/0 handle=0xb7728c00
  | state=R schedstat=( 0 0 0 ) utm=6486 stm=688 core=3 HZ=100
  | stack=0xbf21d000-0xbf21f000 stackSize=8MB
  | held mutexes= "mutator lock"(shared held)
  at com.sushihangover.mylibrary.CrashMe$3.run(CrashMe.java:26)
  at android.os.Handler.handleCallback(Handler.java:739)
  at android.os.Handler.dispatchMessage(Handler.java:95)
  at android.os.Looper.loop(Looper.java:148)
  at android.app.ActivityThread.main(ActivityThread.java:5417)
  at java.lang.reflect.Method.invoke!(Native method)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

"Signal Catcher" daemon prio=5 tid=2 Runnable
  | group="system" sCount=0 dsCount=0 obj=0x12c840a0 self=0xae4c2000
  | sysTid=4488 nice=0 cgrp=default sched=0/0 handle=0xb3432930
  | state=R schedstat=( 0 0 0 ) utm=0 stm=0 core=0 HZ=100
  | stack=0xb3336000-0xb3338000 stackSize=1014KB
  | held mutexes= "mutator lock"(shared held)
  native: #00 pc 0058bd02  /system/lib/libart.so (art::DumpNativeStack(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, int, char const*, art::ArtMethod*, void*)+226)
  native: #01 pc 0055194e  /system/lib/libart.so (art::Thread::Dump(std::__1::basic_ostream<char, std::__1::char_traits<char> >&) const+286)
 ~~~~

【讨论】:

  • 嘿,我有几个问题-希望您有时间回答:) 1. 我尝试从DllImport 捕获异常(第一个示例)并尝试捕获块不起作用-这是正常的行为? 2. 第二个例子(不是按钮的现有属性)生成异常,但异常对象中的堆栈跟踪只有 C# 的堆栈帧 - 我错过了什么吗? 3. 我觉得 Xamarin 中的异常对象错过了很多重要信息 - 现在要获取更多信息,我必须转到基础对象,然后再转到基础,然后我有一些关于异常的信息。我做错了吗?
  • 1) 是的,运行时崩溃无法捕获 2) 缺少什么?不,我不确定您在寻找什么,因为堆栈会指出您代码中的问题。您还在寻找什么更多信息? 3) 不确定您在 CIL 托管异常方面正在寻找什么......如果您正在寻找 CIL 托管异常中的 Java 到 JNI/Mono 到 C#,那么可以捕获和报告的内容是有限制的完整的 Java 异常堆栈,因为它不会映射到 CIL 异常。
  • 在我的项目中,我需要捕获完整的堆栈跟踪(例如:如果我调用 java 类并从 java 代码中获取异常,我想在一个异常对象中捕获来自 C# 和 java 的堆栈跟踪)。可以在 Xamarin 中实现吗?在其他情况下 - 如何从 Xamarin 调用的 Java 捕获异常堆栈跟踪?我想将堆栈跟踪发送到 API 或将异常保存到文件...
  • @mersey 我假设您不是在查看 Java 中的已检查异常,而是查看运行时异常。未经检查的异常 (RuntimeException) 将导致线程终止,现在所有 C#->Java 调用都已被 Xamarin.Android 尝试/捕获,而托管异常就是你得到的,对于抛出的 Java->C# 调用,您可以使用 AndroidEnvironment.UnhandledExceptionRaiser 来检查异常。如果您从 C# 调用到您自己的 Java 代码,请尽可能使用已检查的异常并在 Java 中处理它们
  • @mersey 对于 Java 运行时异常,您需要自己处理 JNI 调用,但我认为这样做没有任何好处。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-22
  • 2021-12-28
  • 1970-01-01
  • 1970-01-01
  • 2011-04-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多