【发布时间】: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 秒报告一次。这用于反映实际应用中发生的错误。这恰好以与实际应用程序相同的方式出错。将周期性间隔设置得较高,以便更快地发现错误以进行调试。
分别在41 和42 周围可以看到:
// 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 错误。
我尝试过的...好吧,除了在FileInfo 和UnixFileInfo 之间来回切换没什么,问题是,我什至不知道该尝试什么。
这是我第一个针对 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 <path/to/exe> 所没有的东西 - 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
是什么让我的应用程序最终以SIGSEGV 或System.NullReferenceException 出错?
【问题讨论】:
标签: c# linux mono posix mono-posix