应用程序运行,但最终以SIGSEGV或NullReferenceException结束

时间:2014-12-03 21:37:40

标签: c# linux mono posix mono-posix

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

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秒报告一次。这用于反映实际应用程序中发生的错误。这种情况与实际应用程序的错误相同。周期性间隔设置为高,以便更快地获得错误以进行调试。

可以在41&amp;线附近看到。 42分别为:

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

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

当使用本机解决方案(FileInfo)运行时 - 没有问题,一切都按预期工作。

现在,当使用等效Mono.PosixUnixFileInfo)时,软件最终会以System.NullReferenceException或更差 - SIGSEGV结束。

System.NullReferenceException案例:

Unhandled Exception:
System.NullReferenceException: Object reference not set to an instance of an object
  at System.String.IndexOfAnyUnchecked (System.Char[] anyOf, Int32 startIndex, Int32 count) [0x00000] in :0
  at System.String.IndexOfAny (System.Char[] anyOf) [0x00019] in /build/mono/src/mono-3.10.0/mcs/class/corlib/System/String.cs:896
  at Mono.Unix.UnixPath.CheckPath (System.String path) [0x00000] in :0
  at Mono.Unix.UnixFileSystemInfo..ctor (System.String path) [0x00000] in :0
  at Mono.Unix.UnixFileInfo..ctor (System.String path) [0x00000] in :0
  at StoneOS.ResourceMonitor.Program.CPUUsage () [0x00008] in /home/stone/sandbox/StoneOS.ResourceMonitor/StoneOS.ResourceMonitor/Program.cs:44
  at StoneOS.ResourceMonitor.Program.m__0 (System.Object state) [0x00001] in /home/stone/sandbox/StoneOS.ResourceMonitor/StoneOS.ResourceMonitor/Program.cs:20
  at System.Threading.Timer+Scheduler.TimerCB (System.Object o) [0x00007] in /build/mono/src/mono-3.10.0/mcs/class/corlib/System.Threading/Timer.cs:317

SIGSEGV案例:

Stacktrace:


Native stacktrace:

        /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]

Debug info from gdb:

warning: File "/usr/bin/mono-sgen-gdb.py" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
        add-auto-load-safe-path /usr/bin/mono-sgen-gdb.py
line to your configuration file "/root/.gdbinit".
To completely disable this security protection add
        set auto-load safe-path /
line to your configuration file "/root/.gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual.  E.g., run from the shell:
        info "(gdb)Auto-loading safe path"
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
[New LWP 441]
[New LWP 440]
[New LWP 439]
[New LWP 438]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
0x00007f2d97ebed57 in sigsuspend () from /usr/lib/libc.so.6
  Id   Target Id         Frame
  5    Thread 0x7f2d95c9b700 (LWP 438) "Finalizer" 0x00007f2d97ebed57 in sigsuspend () from /usr/lib/libc.so.6
  4    Thread 0x7f2d956ff700 (LWP 439) "Timer-Scheduler" 0x00007f2d97ebed57 in sigsuspend () from /usr/lib/libc.so.6
  3    Thread 0x7f2d954fe700 (LWP 440) "Threadpool moni" 0x00007f2d97ebed57 in sigsuspend () from /usr/lib/libc.so.6
  2    Thread 0x7f2d954bd700 (LWP 441) "Threadpool work" 0x00007f2d9823ddeb in waitpid () from /usr/lib/libpthread.so.0
* 1    Thread 0x7f2d98c48780 (LWP 437) "mono" 0x00007f2d97ebed57 in sigsuspend () from /usr/lib/libc.so.6

Thread 5 (Thread 0x7f2d95c9b700 (LWP 438)):
#0  0x00007f2d97ebed57 in sigsuspend () from /usr/lib/libc.so.6
#1  0x00007f2d9864cd46 in ?? () from /usr/lib/libmonosgen-2.0.so.1
#2  
#3  0x00007f2d9823c90e in sem_wait () from /usr/lib/libpthread.so.0
#4  0x00007f2d986ab5c6 in mono_sem_wait () from /usr/lib/libmonosgen-2.0.so.1
#5  0x00007f2d98624529 in ?? () from /usr/lib/libmonosgen-2.0.so.1
#6  0x00007f2d986069e7 in ?? () from /usr/lib/libmonosgen-2.0.so.1
#7  0x00007f2d986b0dd5 in ?? () from /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

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

Thread 3 (Thread 0x7f2d954fe700 (LWP 440)):
#0  0x00007f2d97ebed57 in sigsuspend () from /usr/lib/libc.so.6
#1  0x00007f2d9864cd46 in ?? () from /usr/lib/libmonosgen-2.0.so.1
#2  
#3  0x00007f2d97f802ca in clock_nanosleep () from /usr/lib/libc.so.6
#4  0x00007f2d9869df48 in ?? () from /usr/lib/libmonosgen-2.0.so.1
#5  0x00007f2d98609abe in ?? () from /usr/lib/libmonosgen-2.0.so.1
#6  0x00007f2d986069e7 in ?? () from /usr/lib/libmonosgen-2.0.so.1
#7  0x00007f2d986b0dd5 in ?? () from /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

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

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

=================================================================
Got a SIGSEGV while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries
used by your application.
=================================================================

Aborted (core dumped)

但大多数时候,它都是SIGSEGV错误。

我尝试过的......好吧,除了来自FileInfoUnixFileInfo来回切换,问题是,我不知道甚至尝试什么

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

我已经完成了Mono GDB debugging guide - 设置提供的 .gdbinit,与gdb一起运行 - 结果:

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff47fe700 (LWP 4558)]
0x00007ffff7a1cc47 in ?? () from /usr/lib/libmonosgen-2.0.so.1
(gdb) mono_backtrace 15
#0  0x00007ffff7a1cc47 in ?? () from /usr/lib/libmonosgen-2.0.so.1
#1  0x00007ffff7a1caf8 in ?? () from /usr/lib/libmonosgen-2.0.so.1
#2  0x00007ffff79f236d in ?? () from /usr/lib/libmonosgen-2.0.so.1
#3  0x00007ffff79f7725 in ?? () from /usr/lib/libmonosgen-2.0.so.1
#4  0x00007ffff79f8159 in ?? () from /usr/lib/libmonosgen-2.0.so.1
#5  0x00007ffff7a15cb3 in ?? () from /usr/lib/libmonosgen-2.0.so.1
#6  0x00007ffff7a15ed4 in ?? () from /usr/lib/libmonosgen-2.0.so.1
#7  0x00007ffff79cdb16 in mono_array_new_specific ()
   from /usr/lib/libmonosgen-2.0.so.1
#8 0x400135cb in Cannot access memory at address 0xffffffffe00f2410

这提供了默认mono --debug <path/to/exe>没有 - mono_array_new_specific()来电的内容,但我不知道它在这里的相关性。

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

mono -V

Mono JIT compiler version 3.10.0 (tarball Mon Oct  6 20:46:04 UTC 2014)
Copyright (C) 2002-2014 Novell, Inc, Xamarin Inc and Contributors. www.mono-project.com
        TLS:           __thread
        SIGSEGV:       altstack
        Notifications: epoll
        Architecture:  amd64
        Disabled:      none
        Misc:          softdebug
        LLVM:          supported, not enabled.
        GC:            sgen

是什么让我的应用最终最终错误地导致SIGSEGVSystem.NullReferenceException

1 个答案:

答案 0 :(得分:0)

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