How to avoid XmlSerializer failure if process environment is too large? - c#

I ran into a strange problem last week. A call to new XMLSerializer(typeof(MyType)) crashed with an ExternalException, telling me that csc.exe could not be executed.
After some investigation I found that this exception only occurs if the process environment size reaches a "critical" limit. I created a little sample application to verify that reason.
namespace EnvironmentTester
{
public class Program
{
private static void Main(string[] args)
{
FillProcessEnvironmentBlock(false);
SerializeDataObject();
}
private static void SerializeDataObject()
{
var dto = new DataObject {Name = "MyDto"};
try
{
var xmlSerializer = new XmlSerializer(dto.GetType()); // throws exception
xmlSerializer.Serialize(TextWriter.Null, dto);
Console.WriteLine("No exception occured.");
}
catch(Exception e)
{
Console.WriteLine("Exception occured : " + e.GetType());
}
Console.ReadKey();
}
private static void FillProcessEnvironmentBlock(bool fillToMax)
{
var currentEnvVarIndex = 0;
var environmentSize = GetEnvironmentSize();
int criticalEnvironmentSize = fillToMax ? 30692 : 30691;
while (environmentSize < criticalEnvironmentSize)
{
var envVarName = "Env" + currentEnvVarIndex;
var envVarValueLength = (criticalEnvironmentSize - environmentSize - envVarName.Length - 2) % 32000;
Environment.SetEnvironmentVariable(envVarName, new string('a', envVarValueLength));
currentEnvVarIndex++;
environmentSize = GetEnvironmentSize();
}
}
private static int GetEnvironmentSize()
{
var envVars = Environment.GetEnvironmentVariables();
int environmentSize = 0;
foreach (string envKey in envVars.Keys)
{
environmentSize += envKey.Length;
}
foreach (string envVar in envVars.Values)
{
environmentSize += envVar.Length;
}
environmentSize += 2*envVars.Keys.Count; // add the '=' and the '\0'
return environmentSize;
}
public class DataObject
{
[XmlAttribute("ObjectName")]
public string Name { get; set; }
}
}
}
If FillProcessEnvironmentBlock is called with parameter false, the critical size is not reached, if it's called with true, the ExternalException is thrown. I tested it on two different WindowsXP 32bit SP2 machines, with the same result.
I know that csc.exe is called to create a temporary assembly used to read/write the xml file. But I don't know why the call to csc.exe fails if the process environment is too large.
Does anyone know the reason for this exception? And how can I work around it (if I don't want to write my own xml serialization)? Are there any other known problems causeed by a process environment that's too large?

You can precompile serializers using Sgen tool or MSBuild Sgen task

Related

StreamWriter keeps writing to the same file after instantiating it again for another file

I'm trying to implement a simple file-logger object, with the possibility to truncate the file when its size reaches a threshold.
I am using a StreamWriter, which gets written to at each call of the method Log(). In order to decide when to truncate, I am checking the StreamWriter.BaseStream.Length property before each write and, if it is bigger than the threshold, I close the StreamWriter, create a new file and open the StreamWriter on that file.
For example, if I set the threshold to 10Mb files, it will create a new file each 10Mb of written data.
Under normal load (let's say 3-4 seconds between calls to Log()), everything works as it should. However, the product which is going to use this logger will work with lots of data and required logging every 1 second, even less.
The problem is that the logger seems to completely ignore the creation of the new file(and opening the new stream), failing to truncate it and keeps writing to the existing stream.
I also tried to manually compute the stream's length, hoping it would be a problem with the stream, but it does not work.
I have found out that going step by step with the debugger makes it work correctly, but it does not solve my problem. Logging each second seems to make the program skip the UpdateFile() method entirely.
public class Logger
{
private static Logger _logger;
private const string LogsDirectory = "Logs";
private StreamWriter _streamWriter;
private string _path;
private readonly bool _truncate;
private readonly int _maxSizeMb;
private long _currentSize;
//===========================================================//
public static void Set(string filename, bool truncate = false, int maxSizeMb = 10)
{
if (_logger == null)
{
if (filename.Contains('_'))
{
throw new Exception("Filename cannot contain the _ character!");
}
if (filename.Contains('.'))
{
throw new Exception("The filename must not include the extension");
}
_logger = new Logger(filename, truncate, maxSizeMb);
}
}
//===========================================================//
public static void Log(string message, LogType logType = LogType.Info)
{
_logger?.InternalLog(message, logType);
}
//===========================================================//
public static void LogException(Exception ex)
{
_logger?.InternalLogException(ex);
}
//===========================================================//
private Logger(string filename, bool truncate = false, int maxSizeMb = 10)
{
_path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, LogsDirectory, $"{filename}_{DateTimeToPrefix(DateTime.Now)}.log");
if (CheckForExistingLogs())
{
_path = GetLatestLogFilename();
}
_truncate = truncate;
_maxSizeMb = maxSizeMb;
_streamWriter = new StreamWriter(File.Open(_path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite));
_currentSize = _streamWriter.BaseStream.Length;
}
//===========================================================//
private bool CheckForExistingLogs()
{
var directory = Path.GetDirectoryName(_path);
var filename = Path.GetFileNameWithoutExtension(_path);
if (filename.Contains('_'))
{
filename = filename.Split('_').First();
}
return new DirectoryInfo(directory).GetFiles().Any(x => x.Name.ToLower().Contains(filename.ToLower()));
}
//===========================================================//
private string GetLatestLogFilename()
{
var directory = Path.GetDirectoryName(_path);
var filename = Path.GetFileNameWithoutExtension(_path);
if (filename.Contains('_'))
{
filename = filename.Split('_').First();
}
var files = new DirectoryInfo(directory).GetFiles().Where(x => x.Name.ToLower().Contains(filename.ToLower()));
files = files.OrderBy(x => PrefixToDateTime(x.Name.Split('_').Last()));
return files.Last().FullName;
}
//===========================================================//
private void UpdateFile()
{
_streamWriter.Flush();
_streamWriter.Close();
_streamWriter.Dispose();
_streamWriter = StreamWriter.Null;
_path = GenerateNewFilename();
_streamWriter = new StreamWriter(File.Open(_path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite));
_currentSize = _streamWriter.BaseStream.Length;
}
//===========================================================//
private string GenerateNewFilename()
{
var directory = Path.GetDirectoryName(_path);
var oldFilename = Path.GetFileNameWithoutExtension(_path);
if (oldFilename.Contains('_'))
{
oldFilename = oldFilename.Split('_').First();
}
var newFilename = $"{oldFilename}_{DateTimeToPrefix(DateTime.Now)}.log";
return Path.Combine(directory, newFilename);
}
//===========================================================//
private static string DateTimeToPrefix(DateTime dateTime)
{
return dateTime.ToString("yyyyMMddHHmm");
}
//===========================================================//
private static DateTime PrefixToDateTime(string prefix)
{
var year = Convert.ToInt32(string.Join("", prefix.Take(4)));
var month = Convert.ToInt32(string.Join("", prefix.Skip(4).Take(2)));
var day = Convert.ToInt32(string.Join("", prefix.Skip(6).Take(2)));
var hour = Convert.ToInt32(string.Join("", prefix.Skip(8).Take(2)));
var minute = Convert.ToInt32(string.Join("", prefix.Skip(10).Take(2)));
return new DateTime(year, month, day, hour, minute, 0);
}
//===========================================================//
private int ConvertSizeToMb()
{
return Convert.ToInt32(Math.Truncate(_currentSize / 1024f / 1024f));
}
//===========================================================//
public void InternalLog(string message, LogType logType = LogType.Info)
{
if (_truncate && ConvertSizeToMb() >= _maxSizeMb)
{
UpdateFile();
}
var sendMessage = string.Empty;
switch (logType)
{
case LogType.Error:
{
sendMessage += "( E ) ";
break;
}
case LogType.Warning:
{
sendMessage += "( W ) ";
break;
}
case LogType.Info:
{
sendMessage += "( I ) ";
break;
}
}
sendMessage += $"{DateTime.Now:dd.MM.yyyy HH:mm:ss}: {message}";
_streamWriter.WriteLine(sendMessage);
_streamWriter.Flush();
_currentSize += Encoding.ASCII.GetByteCount(sendMessage);
Console.WriteLine(_currentSize);
}
//===========================================================//
public void InternalLogException(Exception ex)
{
if (_truncate && ConvertSizeToMb() >= _maxSizeMb)
{
UpdateFile();
}
var sendMessage = $"( E ) {DateTime.Now:dd.MM.yyyy HH:mm:ss}: {ex.Message}{Environment.NewLine}{ex.StackTrace}";
_streamWriter.WriteLine(sendMessage);
_streamWriter.Flush();
_currentSize += Encoding.ASCII.GetByteCount(sendMessage);
}
}
Usage example:
private static void Main(string[] args)
{
Logger.Set("Log", true, 10);
while (true)
{
Logger.Log("anything");
}
}
Have you ever encountered such a problem before? How can it be solved? Thanks :)
I don't know how much data your application writes to the log each minute. But if the amount is more than 10MB then the method DateTimeToPrefix will return the same name for a second call inside a minute interval. (Well at least for me this is what happens with the code included in the Main method).
I changed the ToString() to include also the seconds and this gives correct amount of data written in the expected files.
private static string DateTimeToPrefix(DateTime dateTime)
{
return dateTime.ToString("yyyyMMddHHmmss");
}
Your code looks "fine", so I'm guessing the issue is being caused by multiple threads accessing the InternalLog method at once. Your code is not thread safe as it does not use any locking mechanisms. The easiest and probably totally sufficient solution for your project is to add a locking object at class level:
private readonly object _lock = new object();
then wrap your whole InternalLog method in a lock(_lock) statement:
public void InternalLog(string message, LogType logType = LogType.Info)
{
lock(_lock)
{
// your existing code
}
}
This is not a perfect solution and could cause a bottleneck especially as you are flushing the StreamWriter in every call to InternalLog. But for now it should be perfectly fine!

MongoDB - Error connecting to 192.xx.xx.xx : Too many open files

I've setup my mongodb (v4.2.8) sharded cluster on ubuntu 18.04 and using c#.net for my application. I have 2 mongoS in my cluster and below is my code to connect to my mongodb shard
public sealed class MongoDBConnection
{
private static MongoClient client1 = null;
public static MongoClient Connection1
{
get
{
if (client1 == null)
{
lock (InstanceLock)
{
if (client1 == null)
{
TimeSpan connectionTime = new TimeSpan(1, 0, 0);
MongoClientSettings settingShard = new MongoClientSettings
{
MaxConnectionPoolSize = 60000,
MinConnectionPoolSize = 30000,
ConnectTimeout = connectionTime,
Server = new MongoServerAddress("xxx.xxx.xx.xx", 27017)
};
client1 = new MongoClient(settingShard);
}
}
}
return client1;
}
}
}
this class contains all settings to my connection and below is my class to perform DB operatoins
public static class MongoDBOperations
{
private static IMongoClient consDB1 = MongoDBConnection.Connection1;
private static IMongoDatabase MyDB1 = consDB1.GetDatabase("DBEmp");
private static IMongoCollection<EmpDetails> Mycollection1= MyDB1.GetCollection<EmpDetails>("tblEmployees");
public static Queue<string> returnEmployeeList(int limit)
{
Queue<string> empQ = new Queue<string>();
try
{
var filter = Builders<EmpDetails>.Filter.Eq("InfoUpdated", 0);
var emp1 = Mycollection1.Find(filter).Limit(limit).ToList();
if (emp1.Count > 0)
{
for (int i = 0; i < emp1.Count; i++)
{
var _Emp = Builders<EmpDetails>.Filter.Eq("EmailAddress", emp1[i].EmailAddress);
var UpdateDef = Builders<EmpDetails>.Update.Set(s => s.InfoUpdated, 1).Set(s => s.InfoUpdatedOn, DateTime.Now.Date);
var result = Mycollection1.UpdateManyAsync(_Emp, UpdateDef);
empQ.Enqueue(emp[i].EmailAddress.ToString());
}
}
else
{
Console.ForegroundColor = ConsoleColor.DarkRed;
Console.WriteLine("No Emp found to update : ");
Console.ForegroundColor = ConsoleColor.White;
}
}
catch (Exception ex)
{
Console.WriteLine("Error in Getting emp from DB: " + ex.Message);
}
return empQ;
}
}
so what I am actually doing is, I am returning a queue from my class and do some stuff on that list. As soon as the records are fetched from database, I update there status "InfoUpdated" to 1 so they are not picked agian.
I am doing this in my multithreaded application and below is the code for that
class Program
{
static Dictionary<string, Queue<string>> Qs = new Dictionary<string, Queue<string>>();
static int elementsInQueue = 100;
static int totalQueues = 200;
static void Main(string[] args)
{
new Program().FillUpQueuesOnStart();
}
void FillUpQueuesOnStart()
{
for (int i = 1; i <= totalQueues; i++)
{
if (!Qs.ContainsKey("q" + i))
Qs.Add("q" + i, new Queue<string>());
Qs["q" + i] = MongoDBOperations.returnEmployeeList(elementsInQueue);
Console.WriteLine("q" + i + " filled on start");
}
}
}
when I run my program, it works fine initially and fills up to 35 to 30 queues, but then gives the following error
Error connecting to xxx.xxx.xx.xxx:27017 :: caused by :: Too many open files
where xxx.xxx.xx.xxx is the IP of one of my shards. I see this error on one of my mongoS' terminal in my cluster. I've searched a lot on this to no help. I even changed the ulimit -n in all of my nodes in my cluster, still to no luck.
Any help would really be greatly appreciated.

OpenXml Excel: throw error in any word after mail address

I read Excel files using OpenXml. all work fine but if the spreadsheet contains one cell that has an address mail and after it a space and another word, such as:
abc#abc.com abc
It throws an exception immediately at the opening of the spreadsheet:
var _doc = SpreadsheetDocument.Open(_filePath, false);
exception:
DocumentFormat.OpenXml.Packaging.OpenXmlPackageException
Additional information:
Invalid Hyperlink: Malformed URI is embedded as a
hyperlink in the document.
There is an open issue on the OpenXml forum related to this problem: Malformed Hyperlink causes exception
In the post they talk about encountering this issue with a malformed "mailto:" hyperlink within a Word document.
They propose a work-around here: Workaround for malformed hyperlink exception
The workaround is essentially a small console application which locates the invalid URL and replaces it with a hard-coded value; here is the code snippet from their sample that does the replacement; you could augment this code to attempt to correct the passed brokenUri:
private static Uri FixUri(string brokenUri)
{
return new Uri("http://broken-link/");
}
The problem I had was actually with an Excel document (like you) and it had to do with a malformed http URL; I was pleasantly surprised to find that their code worked just fine with my Excel file.
Here is the entire work-around source code, just in case one of these links goes away in the future:
void Main(string[] args)
{
var fileName = #"C:\temp\corrupt.xlsx";
var newFileName = #"c:\temp\Fixed.xlsx";
var newFileInfo = new FileInfo(newFileName);
if (newFileInfo.Exists)
newFileInfo.Delete();
File.Copy(fileName, newFileName);
WordprocessingDocument wDoc;
try
{
using (wDoc = WordprocessingDocument.Open(newFileName, true))
{
ProcessDocument(wDoc);
}
}
catch (OpenXmlPackageException e)
{
e.Dump();
if (e.ToString().Contains("The specified package is not valid."))
{
using (FileStream fs = new FileStream(newFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
{
UriFixer.FixInvalidUri(fs, brokenUri => FixUri(brokenUri));
}
}
}
}
private static Uri FixUri(string brokenUri)
{
brokenUri.Dump();
return new Uri("http://broken-link/");
}
private static void ProcessDocument(WordprocessingDocument wDoc)
{
var elementCount = wDoc.MainDocumentPart.Document.Descendants().Count();
Console.WriteLine(elementCount);
}
}
public static class UriFixer
{
public static void FixInvalidUri(Stream fs, Func<string, Uri> invalidUriHandler)
{
XNamespace relNs = "http://schemas.openxmlformats.org/package/2006/relationships";
using (ZipArchive za = new ZipArchive(fs, ZipArchiveMode.Update))
{
foreach (var entry in za.Entries.ToList())
{
if (!entry.Name.EndsWith(".rels"))
continue;
bool replaceEntry = false;
XDocument entryXDoc = null;
using (var entryStream = entry.Open())
{
try
{
entryXDoc = XDocument.Load(entryStream);
if (entryXDoc.Root != null && entryXDoc.Root.Name.Namespace == relNs)
{
var urisToCheck = entryXDoc
.Descendants(relNs + "Relationship")
.Where(r => r.Attribute("TargetMode") != null && (string)r.Attribute("TargetMode") == "External");
foreach (var rel in urisToCheck)
{
var target = (string)rel.Attribute("Target");
if (target != null)
{
try
{
Uri uri = new Uri(target);
}
catch (UriFormatException)
{
Uri newUri = invalidUriHandler(target);
rel.Attribute("Target").Value = newUri.ToString();
replaceEntry = true;
}
}
}
}
}
catch (XmlException)
{
continue;
}
}
if (replaceEntry)
{
var fullName = entry.FullName;
entry.Delete();
var newEntry = za.CreateEntry(fullName);
using (StreamWriter writer = new StreamWriter(newEntry.Open()))
using (XmlWriter xmlWriter = XmlWriter.Create(writer))
{
entryXDoc.WriteTo(xmlWriter);
}
}
}
}
}
The fix by #RMD works great. I've been using it for years. But there is a new fix.
You can see the fix here in the changelog for issue #793
Upgrade OpenXML to 2.12.0.
Right click solution and select Manage NuGet Packages.
Implement the fix
It is helpful to have a unit test. Create an excel file with a bad email address like test#gmail,com. (Note the comma instead of the dot).
Make sure the stream you open and the call to SpreadsheetDocument.Open allows Read AND Write.
You need to implement a RelationshipErrorHandlerFactory and use it in the options when you open. Here is the code I used:
public class UriRelationshipErrorHandler : RelationshipErrorHandler
{
public override string Rewrite(Uri partUri, string id, string uri)
{
return "https://broken-link";
}
}
Then you need to use it when you open the document like this:
var openSettings = new OpenSettings
{
RelationshipErrorHandlerFactory = package =>
{
return new UriRelationshipErrorHandler();
}
};
using var document = SpreadsheetDocument.Open(stream, true, openSettings);
One of the nice things about this solution is that it does not require you to create a temporary "fixed" version of your file and it is far less code.
Unfortunately solution where you have to open file as zip and replace broken hyperlink would not help me.
I just was wondering how it is posible that it works fine when your target framework is 4.0 even if your only installed .Net Framework has version 4.7.2.
I have found out that there is private static field inside System.UriParser that selects version of URI's RFC specification. So it is possible to set it to V2 as it is set for .net 4.0 and lower versions of .Net Framework. Only problem that it is private static readonly.
Maybe someone will want to set it globally for whole application. But I wrote UriQuirksVersionPatcher that will update this version and restore it back in Dispose method. It is obviously not thread-safe but it is acceptable for my purpose.
using System;
using System.Diagnostics;
using System.Reflection;
namespace BarCap.RiskServices.RateSubmissions.Utility
{
#if (NET20 || NET35 || NET40)
public class UriQuirksVersionPatcher : IDisposable
{
public void Dispose()
{
}
}
#else
public class UriQuirksVersionPatcher : IDisposable
{
private const string _quirksVersionFieldName = "s_QuirksVersion"; //See Source\ndp\fx\src\net\System\_UriSyntax.cs in NexFX sources
private const string _uriQuirksVersionEnumName = "UriQuirksVersion";
/// <code>
/// private enum UriQuirksVersion
/// {
/// V1 = 1, // RFC 1738 - Not supported
/// V2 = 2, // RFC 2396
/// V3 = 3, // RFC 3986, 3987
/// }
/// </code>
private const string _oldQuirksVersion = "V2";
private static readonly Lazy<FieldInfo> _targetFieldInfo;
private static readonly Lazy<int?> _patchValue;
private readonly int _oldValue;
private readonly bool _isEnabled;
static UriQuirksVersionPatcher()
{
var targetType = typeof(UriParser);
_targetFieldInfo = new Lazy<FieldInfo>(() => targetType.GetField(_quirksVersionFieldName, BindingFlags.Static | BindingFlags.NonPublic));
_patchValue = new Lazy<int?>(() => GetUriQuirksVersion(targetType));
}
public UriQuirksVersionPatcher()
{
int? patchValue = _patchValue.Value;
_isEnabled = patchValue.HasValue;
if (!_isEnabled) //Disabled if it failed to get enum value
{
return;
}
int originalValue = QuirksVersion;
_isEnabled = originalValue != patchValue;
if (!_isEnabled) //Disabled if value is proper
{
return;
}
_oldValue = originalValue;
QuirksVersion = patchValue.Value;
}
private int QuirksVersion
{
get
{
return (int)_targetFieldInfo.Value.GetValue(null);
}
set
{
_targetFieldInfo.Value.SetValue(null, value);
}
}
private static int? GetUriQuirksVersion(Type targetType)
{
int? result = null;
try
{
result = (int)targetType.GetNestedType(_uriQuirksVersionEnumName, BindingFlags.Static | BindingFlags.NonPublic)
.GetField(_oldQuirksVersion, BindingFlags.Static | BindingFlags.Public)
.GetValue(null);
}
catch
{
#if DEBUG
Debug.WriteLine("ERROR: Failed to find UriQuirksVersion.V2 enum member.");
throw;
#endif
}
return result;
}
public void Dispose()
{
if (_isEnabled)
{
QuirksVersion = _oldValue;
}
}
}
#endif
}
Usage:
using(new UriQuirksVersionPatcher())
{
using(var document = SpreadsheetDocument.Open(fullPath, false))
{
//.....
}
}
P.S. Later I found that someone already implemented this pathcher: https://github.com/google/google-api-dotnet-client/blob/master/Src/Support/Google.Apis.Core/Util/UriPatcher.cs
I haven't use OpenXml but if there's no specific reason for using it then I highly recommend LinqToExcel from LinqToExcel. Example of code is here:
var sheet = new ExcelQueryFactory("filePath");
var allRows = from r in sheet.Worksheet() select r;
foreach (var r in allRows) {
var cella = r["Header"].ToString();
}

Parse input and build a Dictionary/HashMap while parsing

I am not sure if the title makes it clear what I want to do.
My input for my parser contains debug information about C source files. Some of the input looks e.g. like this:
L:C$main.c$41$1$10:C0C5
Which basically means that line 10 in the source file main.c corresponds with the memory address C0C5.
Here is an example what my AST looks like:
Which represents the input:
M:main
L:C$main.c$29$1$0:C09C
L:C$main.c$30$1$10:C0A2
M:divide
L:C$divice.c$31$1$10:C5A9
What I want are two Hash-Maps such that I can access these information quickly at runtime. But how can I now build such Hash-Maps and is it possible to do that at parsetime?
This is how I would like to use my parser:
public CDBFileParser getFileParser(String cdbFilePath)
{
Stream stream = File.OpenRead(cdbFilePath);
ANTLRInputStream inputStream = new ANTLRInputStream(stream);
CDBFileLexer lexer = new CDBFileLexer(inputStream);
CommonTokenStream tokens = new CommonTokenStream(lexer);
CDBFileParser parser = new CDBFileParser(tokens);
try
{
parser.TreeAdaptor = new CommonTreeAdaptor();
parser.parseCDBFile();
// All this works so far. Here comes the part I am looking for:
Modules[] modules = parser.getModules();
Dictionary<int, int> lineToAddress = modules[0].getLineToAddressMap();
Dictionary<int, int> addressToLine = modules[0].getAddressToLineMap();
int address = 0xC09C;
System.Out.WriteLine( "Address 0xC09C is at line " + addressToLine.get(address) + " in " + modules[0].getName() );
}
catch (Exception e)
{
printException(e);
}
return parser;
}
Expected Output:
Address 0xC09C is at line 29 in main
Can anybody help?
Best regards.
I was about to delete my question but maybe somebody else comes along to this post. I just made the transition to ANTLR4 and it really is much simpler (so far at least).
In ANTLR4 an interface (e.g. ICDBFileListener) is built for you which one can use to catch all information at parsetime:
namespace Parser
{
public class CDBFileParserListener : ICDBFileListener
{
public void ExitModule_name(CDBFileParser.Module_nameContext context)
{
Console.WriteLine("ModuleName: " + context.GetText());
// Add module to module-map and remember
// that current module is context.GetText()
}
public void ExitLine_number(CDBFileParser.Line_numberContext context)
{
Console.WriteLine("LineNumber: " + context.GetText());
// Remember line number
}
public void ExitMemory_address(CDBFileParser.Memory_addressContext context)
{
Console.WriteLine("MemoryAddress: " + context.GetText());
// Add linenumber <-> memoryaddress to maps
}
public Modules[] getModules()
{
return m_modules;
}
}
}
And this is how it can be used:
public CDBFileParser getFileParser(String cdbFilePath)
{
Stream stream = File.OpenRead(cdbFilePath);
AntlrInputStream inputStream = new AntlrInputStream(stream);
CDBFileLexer lexer = new CDBFileLexer(inputStream);
CommonTokenStream tokens = new CommonTokenStream(lexer);
CDBFileParser parser = new CDBFileParser(tokens);
try
{
CDBFileParserListener listener = new CDBFileParserListener();
parser.AddParseListener(listener);
System.Diagnostics.Debug.WriteLine(parser.parseCDBFile().ToStringTree());
Dictionary<String, Module> modules = listener.Modules;
Module main;
modules.TryGetValue("main", out main);
long line = main.getLineFromAddress(0xC09C);
Console.WriteLine("0xC09C maps to " + line + " in main.c");
}
catch (Exception e)
{
printException(e);
}
return parser;
}

Retrieve process network usage

How can I get a process send/receive bytes? the preferred way is doing it with C#.
I've searched this a lot and I didn't find any simple solution for this. Some solutions suggested to install the WinPCap on the machine and to work with this lib.
Like this guy asked: Need "Processes with Network Activity" functionality in managed code - Like resmon.exe does it
I don't want the overhead of the lib.
Is there a simple solution for this?
Actually I want the exactly data that the Resource Monitor of Windows gives under the "Processes with Network Activity" tab:
How does the Resource Monitor of Windows gets this information?
Any example?
Also, tried to use the counter method which is mentioned over here:
Missing network sent/received
but with no success - as not every process is shown under this counter.
And again I'm wondering how the Resource Monitor gets this information even without using this counter...
Resource monitor uses ETW - thankfully, Microsoft have created a nice nuget .net wrapper to make it easier to use.
I wrote something like this recently to report back my process's network IO:
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.Diagnostics.Tracing.Parsers;
using Microsoft.Diagnostics.Tracing.Session;
namespace ProcessMonitoring
{
public sealed class NetworkPerformanceReporter : IDisposable
{
private DateTime m_EtwStartTime;
private TraceEventSession m_EtwSession;
private readonly Counters m_Counters = new Counters();
private class Counters
{
public long Received;
public long Sent;
}
private NetworkPerformanceReporter() { }
public static NetworkPerformanceReporter Create()
{
var networkPerformancePresenter = new NetworkPerformanceReporter();
networkPerformancePresenter.Initialise();
return networkPerformancePresenter;
}
private void Initialise()
{
// Note that the ETW class blocks processing messages, so should be run on a different thread if you want the application to remain responsive.
Task.Run(() => StartEtwSession());
}
private void StartEtwSession()
{
try
{
var processId = Process.GetCurrentProcess().Id;
ResetCounters();
using (m_EtwSession = new TraceEventSession("MyKernelAndClrEventsSession"))
{
m_EtwSession.EnableKernelProvider(KernelTraceEventParser.Keywords.NetworkTCPIP);
m_EtwSession.Source.Kernel.TcpIpRecv += data =>
{
if (data.ProcessID == processId)
{
lock (m_Counters)
{
m_Counters.Received += data.size;
}
}
};
m_EtwSession.Source.Kernel.TcpIpSend += data =>
{
if (data.ProcessID == processId)
{
lock (m_Counters)
{
m_Counters.Sent += data.size;
}
}
};
m_EtwSession.Source.Process();
}
}
catch
{
ResetCounters(); // Stop reporting figures
// Probably should log the exception
}
}
public NetworkPerformanceData GetNetworkPerformanceData()
{
var timeDifferenceInSeconds = (DateTime.Now - m_EtwStartTime).TotalSeconds;
NetworkPerformanceData networkData;
lock (m_Counters)
{
networkData = new NetworkPerformanceData
{
BytesReceived = Convert.ToInt64(m_Counters.Received / timeDifferenceInSeconds),
BytesSent = Convert.ToInt64(m_Counters.Sent / timeDifferenceInSeconds)
};
}
// Reset the counters to get a fresh reading for next time this is called.
ResetCounters();
return networkData;
}
private void ResetCounters()
{
lock (m_Counters)
{
m_Counters.Sent = 0;
m_Counters.Received = 0;
}
m_EtwStartTime = DateTime.Now;
}
public void Dispose()
{
m_EtwSession?.Dispose();
}
}
public sealed class NetworkPerformanceData
{
public long BytesReceived { get; set; }
public long BytesSent { get; set; }
}
}
You can use PerformanceCounter. Sample code:
//Define
string pn = "MyProcessName.exe";
var readOpSec = new PerformanceCounter("Process","IO Read Operations/sec", pn);
var writeOpSec = new PerformanceCounter("Process","IO Write Operations/sec", pn);
var dataOpSec = new PerformanceCounter("Process","IO Data Operations/sec", pn);
var readBytesSec = new PerformanceCounter("Process","IO Read Bytes/sec", pn);
var writeByteSec = new PerformanceCounter("Process","IO Write Bytes/sec", pn);
var dataBytesSec = new PerformanceCounter("Process","IO Data Bytes/sec", pn);
var counters = new List<PerformanceCounter>
{
readOpSec,
writeOpSec,
dataOpSec,
readBytesSec,
writeByteSec,
dataBytesSec
};
// get current value
foreach (PerformanceCounter counter in counters)
{
float rawValue = counter.NextValue();
// display the value
}
And this is to get performance counters for the Network card. Note it is not process specific
string cn = "get connection string from WMI";
var networkBytesSent = new PerformanceCounter("Network Interface", "Bytes Sent/sec", cn);
var networkBytesReceived = new PerformanceCounter("Network Interface", "Bytes Received/sec", cn);
var networkBytesTotal = new PerformanceCounter("Network Interface", "Bytes Total/sec", cn);
Counters.Add(networkBytesSent);
Counters.Add(networkBytesReceived);
Counters.Add(networkBytesTotal);
Have a look at the IP Helper API. There is an implementation in C# by Simon Mourier that sums transferred bytes per process: https://stackoverflow.com/a/25650933/385513
It would be interesting to know how this compares with Event Tracing for Windows (ETW)...

Categories

Resources