【问题标题】:Application runs, but eventually ends up with SIGSEGV or NullReferenceException应用程序运行,但最终以 SIGSEGV 或 NullReferenceException 结束
【发布时间】:2014-12-03 21:37:39
【问题描述】:

我有以下程序(完整源代码):

using Mono.Unix;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;

namespace StoneOS.ResourceMonitor
{
    class Program
    {
        static void Main(string[] args)
        {
            var runner = new Timer(state =>
            {
                var cpu = CPUUsage();
                var ram = RAMUsage();
                var hdd = DriveUsage("/dev/sda3");

                Console.WriteLine("[{0:O}] C: {1:N2} | R: {2:N2} | S: {3:N2} | H: {4:N2}", DateTime.Now, cpu, ram.Total, ram.Swap, hdd);
            }, null, TimeSpan.Zero, TimeSpan.FromMilliseconds(50));

            Console.ReadKey(true);
        }

        private static string LastReadCPULine = null;

        struct Jiffy
        {
            public double Total;

            public double Work;
        }

        public static double? CPUUsage()
        {
            // File path and descriptor, that holds the actual CPU data.
            var statsFile = "/proc/stat";
            // var unixInfo = new FileInfo(statsFile);
            var unixInfo = new UnixFileInfo(statsFile);

            // Read last queried data into cache.
            string last = LastReadCPULine;

            // Prepare for new read.
            List<string> output = new List<string>();

            // Read data.
            using (var reader = new StreamReader(unixInfo.OpenRead()))
            {
                string currentLine;

                while ((currentLine = reader.ReadLine()) != null)
                {
                    output.Add(currentLine);
                }
            }

            unixInfo = null; // clear...

            // Select the first entry, that should be total of CPU.
            string current = LastReadCPULine = output.First();

            // If there was no last entry, that means we cannot calculate - return zero.
            if (last == null)
            {
                return null;
            }

            return CalculateCPUUsage(last, current);
        }

        private static double CalculateCPUUsage(string last, string current)
        {
            Jiffy lastJiffy = GetJiffies(last);
            Jiffy currentJiffy = GetJiffies(current);

            double work = currentJiffy.Work - lastJiffy.Work;
            double total = currentJiffy.Total - lastJiffy.Total;

            return (work / total) * 100;
        }

        private static Jiffy GetJiffies(string statLine)
        {
            // Split on spaces.
            string[] parts = subsequentSpacePattern.Split(statLine);

            // Remove first entry (label - cpu).
            IEnumerable<double> convertedData = parts.Skip(1).Select(entry => Convert.ToDouble(entry));

            // Calculate the values for the Jiffy.
            return new Jiffy
            {
                Total = convertedData.Sum(),
                Work = convertedData.Take(3).Sum()
            };
        }

        struct Memory
        {
            public double Total;
            public double Swap;
        }

        private static Memory RAMUsage()
        {
            var processInfo = new ProcessStartInfo("/bin/free", "-b")
            {
                RedirectStandardError = true,
                RedirectStandardInput = true,
                RedirectStandardOutput = true,
                UseShellExecute = false,
                CreateNoWindow = true
            };

            List<string> output = new List<string>();

            using (var process = Process.Start(processInfo))
            {
                process.WaitForExit();

                string currentLine;

                using (var reader = process.StandardOutput)
                {
                    while ((currentLine = reader.ReadLine()) != null)
                    {
                        output.Add(currentLine);
                    }
                }
            }

            return new Memory
            {
                Total = CalculateTotalMemoryUsage(output.Skip(1).Take(1).Single()),
                Swap = CalculateSwapUsage(output.Skip(2).Take(1).Single())
            };
        }

        private static Regex subsequentSpacePattern = new Regex(@"\s+");

        private static double CalculateTotalMemoryUsage(string memoryLine)
        {
            string[] parts = subsequentSpacePattern.Split(memoryLine);

            // Console.WriteLine("[{0:O}] Memory: {1}", DateTime.Now, memoryLine);
            // Console.WriteLine("[{0:O}] Memory: {1}", DateTime.Now, String.Join(", ", parts));

            string totalByteString = parts.Skip(1).Take(1).Single();
            string availableByteString = parts.Last();

            // Console.WriteLine("[{0:O}] Total: '{1}'; Available: '{2}'", DateTime.Now, totalByteString, availableByteString);

            double total = Convert.ToDouble(totalByteString);
            double available = Convert.ToDouble(availableByteString);

            var percentage = (available / total) * 100d;

            // Console.WriteLine("[{0:O}] Memory %: {1} (Total: {2}, Free: {3})", DateTime.Now, percentage, total, available);

            return 100d - percentage;
        }

        private static double CalculateSwapUsage(string swapLine)
        {
            string[] parts = subsequentSpacePattern.Split(swapLine);

            // Console.WriteLine("[{0:O}] Swap: {1}", DateTime.Now, swapLine);
            // Console.WriteLine("[{0:O}] Swap: {1}", DateTime.Now, String.Join(", ", parts));

            string totalByteString = parts.Skip(1).Take(1).Single();
            string freeByteString = parts.Last();

            // Console.WriteLine("[{0:O}] Total: '{1}'; Free: '{2}'", DateTime.Now, totalByteString, freeByteString);

            double total = Convert.ToDouble(totalByteString);
            double free = Convert.ToDouble(freeByteString);

            var percentage = (free / total) * 100d;

            // Console.WriteLine("[{0:O}] Swap %: {1} (Total: {2}, Free: {3})", DateTime.Now, percentage, total, free);

            // We are interested in remainder.
            return 100d - percentage;
        }

        private static Regex multiSpacePattern = new Regex(@"\s+");

        private static double DriveUsage(string drive)
        {
            var processInfo = new ProcessStartInfo("/bin/df", String.Format("-B1 {0}", drive))
            {
                RedirectStandardError = true,
                RedirectStandardInput = true,
                RedirectStandardOutput = true,
                UseShellExecute = false,
                CreateNoWindow = true
            };

            List<string> output = new List<string>();

            using (var process = Process.Start(processInfo))
            {
                process.WaitForExit();

                string currentLine;

                using (var reader = process.StandardOutput)
                {
                    while ((currentLine = reader.ReadLine()) != null)
                    {
                        output.Add(currentLine);
                    }
                }
            }

            // Second output line is the one we're looking for.
            var second = output.Last();

            string[] parts = multiSpacePattern.Split(second);

            if (parts.Length != 6)
            {
                throw new ApplicationException(String.Format("Invalid column count in df's output: {0}", second));
            }

            var percentage = parts[4].TrimEnd('%');

            // Console.WriteLine("Output: {0}", second);
            // Console.WriteLine("[4] = {0}, percentage = {1}", parts[4], percentage);

            return Convert.ToDouble(percentage);
        }
    }
}

只是一个基本的资源监视器,它从系统读取/请求一些信息并每 N 秒报告一次。这用于反映实际应用中发生的错误。这恰好以与实际应用程序相同的方式出错。将周期性间隔设置得较高,以便更快地发现错误以进行调试。

分别在4142 周围可以看到:

// var unixInfo = new FileInfo(statsFile);
var unixInfo = new UnixFileInfo(statsFile);

我设置了两个解决方案。一个使用原生 .NET/Mono 本身,另一个 - Mono.Posix

使用本机解决方案 (FileInfo) 运行时 - 没有问题,一切正常。

现在,当使用 Mono.Posix 等效项 (UnixFileInfo) 运行时,该软件最终会以 System.NullReferenceException 或更糟 - SIGSEGV 结束。

System.NullReferenceException案例:

未处理的异常:
System.NullReferenceException:对象引用未设置为对象的实例
  在 System.String.IndexOfAnyUnchecked (System.Char[] anyOf, Int32 startIndex, Int32 count) [0x00000] in :0
  在 System.String.IndexOfAny (System.Char[] anyOf) [0x00019] 在 /build/mono/src/mono-3.10.0/mcs/class/corlib/System/String.cs:896
  在 Mono.Unix.UnixPath.CheckPath (System.String path) [0x00000] in :0
  在 Mono.Unix.UnixFileSystemInfo..ctor (System.String path) [0x00000] in :0
  在 Mono.Unix.UnixFileInfo..ctor (System.String 路径) [0x00000] in :0
  在 /home/stone/sandbox/StoneOS.ResourceMonitor/StoneOS.ResourceMonitor/Program.cs:44 中的 StoneOS.ResourceMonitor.Program.CPUUsage () [0x00008]
  在 /home/stone/sandbox/StoneOS.ResourceMonitor/StoneOS.ResourceMonitor/Program.cs:20 中的 StoneOS.ResourceMonitor.Program.m__0(System.Object 状态)[0x00001]
  在 System.Threading.Timer+Scheduler.TimerCB (System.Object o) [0x00007] 在 /build/mono/src/mono-3.10.0/mcs/class/corlib/System.Threading/Timer.cs:317上一页>

SIGSEGV案例:

堆栈跟踪:


本机堆栈跟踪:

        /usr/lib/libmonosgen-2.0.so.1(+0xd546a) [0x7f2d9851f46a]
        /usr/lib/libmonosgen-2.0.so.1(+0x133aeb) [0x7f2d9857daeb]
        /usr/lib/libmonosgen-2.0.so.1(+0x3fde6) [0x7f2d98489de6]
        /usr/lib/libpthread.so.0(+0x10200) [0x7f2d9823e200]
        /usr/lib/libmonosgen-2.0.so.1(+0x204d70) [0x7f2d9864ed70]
        /usr/lib/libmonosgen-2.0.so.1(+0x20bd4f) [0x7f2d98655d4f]
        /usr/lib/libmonosgen-2.0.so.1(+0x20c159) [0x7f2d98656159]
        /usr/lib/libmonosgen-2.0.so.1(+0x229cb3) [0x7f2d98673cb3]
        /usr/lib/libmonosgen-2.0.so.1(+0x229d7f) [0x7f2d98673d7f]
        [0x4150ff33]

来自 gdb 的调试信息:

警告:文件“/usr/bin/mono-sgen-gdb.py”自动加载已被您的“自动加载安全路径”设置为“$debugdir:$datadir/auto-load”拒绝。
要启用此文件的执行,请添加
        添加自动加载安全路径/usr/bin/mono-sgen-gdb.py
行到您的配置文件“/root/.gdbinit”。
要完全禁用此安全保护,请添加
        设置自动加载安全路径 /
行到您的配置文件“/root/.gdbinit”。
有关此安全保护的更多信息,请参阅
GDB 手册中的“自动加载安全路径”部分。例如,从 shell 运行:
        info "(gdb)自动加载安全路径"
警告:无法为 linux-vdso.so.1 加载共享库符号。
你需要“set solib-search-path”还是“set sysroot”?
[新 LWP 441]
[新 LWP 440]
[新 LWP 439]
[新 LWP 438]
[启用使用 libthread_db 进行线程调试]
使用主机 libthread_db 库“/usr/lib/libthread_db.so.1”。
/usr/lib/libc.so.6 中的 sigsuspend () 中的 0x00007f2d97ebed57
  Id 目标 Id 框架
  5 线程 0x7f2d95c9b700 (LWP 438) “终结器” 0x00007f2d97ebed57 在来自 /usr/lib/libc.so.6 的 sigsuspend ()
  4 线程 0x7f2d956ff700 (LWP 439) "Timer-Scheduler" 0x00007f2d97ebed57 在来自 /usr/lib/libc.so.6 的 sigsuspend ()
  3 线程 0x7f2d954fe700 (LWP 440) "Threadpool moni" 0x00007f2d97ebed57 在来自 /usr/lib/libc.so.6 的 sigsuspend ()
  2 线程 0x7f2d954bd700 (LWP 441) "线程池工作" 0x00007f2d9823ddeb 在来自 /usr/lib/libpthread.so.0 的 waitpid ()
* 1 线程 0x7f2d98c48780 (LWP 437) "mono" 0x00007f2d97ebed57 在来自 /usr/lib/libc.so.6 的 sigsuspend ()

线程 5(线程 0x7f2d95c9b700 (LWP 438)):
#0 0x00007f2d97ebed57 in sigsuspend () from /usr/lib/libc.so.6
#1 0x00007f2d9864cd46 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#2
#3 0x00007f2d9823c90e 来自 /usr/lib/libpthread.so.0 的 sem_wait ()
#4 0x00007f2d986ab5c6 in mono_sem_wait () from /usr/lib/libmonosgen-2.0.so.1
#5 0x00007f2d98624529 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#6 0x00007f2d986069e7 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#7 0x00007f2d986b0dd5 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#8 0x00007f2d98235314 in start_thread () from /usr/lib/libpthread.so.0
#9 0x00007f2d97f733ed in clone () from /usr/lib/libc.so.6

线程 4(线程 0x7f2d956ff700 (LWP 439)):
#0 0x00007f2d97ebed57 in sigsuspend () from /usr/lib/libc.so.6
#1 0x00007f2d9864cd46 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#2
#3 0x00007f2d9823ac68 in pthread_cond_timedwait@@GLIBC_2.3.2 () 来自 /usr/lib/libpthread.so.0
#4 0x00007f2d986885ba 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#5 0x00007f2d9869c9c2 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#6 0x00007f2d9860642f 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#7 0x00007f2d986078dc 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#8 0x000000004152a6ad 在?? ()
#9 0x0000000000000001 在?? ()
#10 0x0000000000000001 在 ?? ()
#11 0x0000000000000032 在?? ()
#12 0x00007f2d97002cd0 在?? ()
#13 0x00000000000000031 在 ?? ()
#14 0x00007f2d880025e0 在?? ()
#15 0x00007f2d956febb0 在?? ()
#16 0x00007f2d956fe9f0 在?? ()
#17 0x00007f2d956fe950 在?? ()
#18 0x000000004152a418 在?? ()
#19 0x0000000000000000 在 ?? ()

线程 3(线程 0x7f2d954fe700 (LWP 440)):
#0 0x00007f2d97ebed57 in sigsuspend () from /usr/lib/libc.so.6
#1 0x00007f2d9864cd46 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#2
#3 0x00007f2d97f802ca in clock_nanosleep () from /usr/lib/libc.so.6
#4 0x00007f2d9869df48 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#5 0x00007f2d98609abe 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#6 0x00007f2d986069e7 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#7 0x00007f2d986b0dd5 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#8 0x00007f2d98235314 in start_thread () from /usr/lib/libpthread.so.0
#9 0x00007f2d97f733ed in clone () from /usr/lib/libc.so.6

线程 2(线程 0x7f2d954bd700 (LWP 441)):
#0 0x00007f2d9823ddeb in waitpid () from /usr/lib/libpthread.so.0
#1 0x00007f2d9851f500 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#2 0x00007f2d9857daeb 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#3 0x00007f2d98489de6 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#4
#5 0x00007f2d9864ed70 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#6 0x00007f2d98655d4f 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#7 0x00007f2d98656159 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#8 0x00007f2d98673cb3 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#9 0x00007f2d98673d7f 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#10 0x000000004150ff33 在?? ()
#11 0x00007f2d973fffa0 在 ?? ()
#12 0x00007f2d973ff108 在?? ()
#13 0x000000000000001d 在 ?? ()
#14 0x00007f2d973ff108 在?? ()
#15 0x0000000000000001 在 ?? ()
#16 0x00007f2d800025e0 在?? ()
#17 0x000000000000001f 在 ?? ()
#18 0x00007f2d954bc6d0 在?? ()
#19 0x00007f2d954bc5e0 在?? ()
#20 0x0000000041557efc 在 ?? ()
#21 0x00007f2d97000d30 在?? ()
#22 0x00007f2d973fffa0 在?? ()
#23 0x00007f2d973f1ae8 在?? ()
#24 0x000000000000001f 在 ?? ()
#25 0x0000000000000001 在 ?? ()
#26 0x000000000000001d 在 ?? ()
#27 0x00007f2d954bd680 在?? ()
#28 0x00007f2d98673d3f 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#29 0x000000004150ff33 在?? ()
#30 0x00007f2d97000d30 在?? ()
#31 0x00007f2d973f1ae8 在?? ()
#32 0x00000000000000001 在?? ()
#33 0x0000000000000025 在?? ()
#34 0x00007f2d973f1ae8 在?? ()
#35 0x00007f2d97000d30 在?? ()
#36 0x00007f2d973f1ae8 在?? ()
#37 0x00007f2d97000d30 在?? ()
#38 0x00007f2d954bc770 在?? ()
#39 0x00000000415577e4 在?? ()
#40 0x000000000000001f 在 ?? ()
#41 0x0000000000000001 在 ?? ()
#42 0x00000000000000001 在?? ()
#43 0x000000000000001c 在 ?? ()
#44 0x0000000000000001 在?? ()
#45 0x00000000000000025 在 ?? ()
#46 0x000000000000001f 在 ?? ()
#47 0x0000000000000001 在?? ()
#48 0x00000000000000001 在?? ()
#49 0x00007f2d973f1ae8 在?? ()
#50 0x00007f2d973fffa0 在 ?? ()
#51 0x0000000000000000 在 ?? ()

线程 1(线程 0x7f2d98c48780 (LWP 437)):
#0 0x00007f2d97ebed57 in sigsuspend () from /usr/lib/libc.so.6
#1 0x00007f2d9864cd46 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#2
#3 0x00007f2d9823d3bb in read () from /usr/lib/libpthread.so.0
#4 0x00007f2d986897e1 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#5 0x00007f2d985ac234 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#6 0x0000000041530c61 在?? ()
#7 0x00007f2d970168c0 在?? ()
#8 0x0000000000000000 在 ?? ()

==================================================== ================
执行本机代码时获得 SIGSEGV。这通常表明
单声道运行时或本机库之一中的致命错误
由您的应用程序使用。
==================================================== ================

中止(核心转储)

不过,大多数时候是SIGSEGV 错误。

我尝试过的...好吧,除了在FileInfoUnixFileInfo 之间来回切换没什么,问题是,我什至不知道该尝试什么

这是我第一个针对 Linux 的主要 Mono 应用程序,因此不知道在这里做什么。

我已经走过Mono GDB debugging guide - 设置提供 .gdbinit,运行gdb - 结果:

程序收到信号 SIGSEGV,分段错误。
[切换到线程 0x7ffff47fe700 (LWP 4558)]
0x00007ffff7a1cc47 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
(gdb) mono_backtrace 15
#0 0x00007ffff7a1cc47 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#1 0x00007ffff7a1caf8 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#2 0x00007ffff79f236d 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#3 0x00007ffff79f7725 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#4 0x00007ffff79f8159 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#5 0x00007ffff7a15cb3 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#6 0x00007ffff7a15ed4 在?? () 来自 /usr/lib/libmonosgen-2.0.so.1
#7 0x00007ffff79cdb16 在 mono_array_new_specific ()
   来自 /usr/lib/libmonosgen-2.0.so.1
#8 0x400135cb in Cannot access memory at address 0xffffffffe00f2410

这提供了默认 mono --debug &lt;path/to/exe&gt; 所没有的东西 - mono_array_new_specific() 调用,不过,我不知道它在这里有什么相关性。

我不确定这是 Mono.Posix 库中的错误还是其他问题,但这真的让我很烦恼。是的,对于这个特殊的示例,我可以使用FileInfo,但在实际应用中我实际上使用的是UnixSymbolicLinkInfo,这会导致同样的问题。

mono -V:

Mono JIT 编译器版本 3.10.0(tarball Mon Oct 6 20:46:04 UTC 2014)
版权所有 (C) 2002-2014 Novell, Inc、Xamarin Inc 和贡献者。 www.mono-project.com
        TLS:__thread
        SIGSEGV:altstack
        通知:epoll
        架构:amd64
        禁用:无
        杂项:软调试
        LLVM:支持,未启用。
        GC:sgen

是什么让我的应用程序最终SIGSEGVSystem.NullReferenceException 出错?

【问题讨论】:

    标签: c# linux mono posix mono-posix


    【解决方案1】:

    在类库(System.String 类)中绝不应该发生 NullReferenceException,因此这一定是一个错误。请在http://bugzilla.xamarin.com/ 报告。理想情况下,您应该在报告中包含调用 IndexOfAny() 方法时使用的字符串和使用的参数。为此,您可能需要检查 Mono.Posix 的源代码,以了解 Mono.Unix.UnixPath.CheckPath() 内部做了什么。

    【讨论】:

      猜你喜欢
      • 2023-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多