golang.zx2c4.com/wireguard/windows@v0.5.4-0.20230123132234-dcc0eb72a04b/embeddable-dll-service/csharp/TunnelDll/Ringlogger.cs (about) 1 /* SPDX-License-Identifier: MIT 2 * 3 * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved. 4 */ 5 6 using System; 7 using System.IO; 8 using System.IO.MemoryMappedFiles; 9 using System.Text; 10 using System.Collections.Generic; 11 using System.Threading; 12 using System.Runtime.CompilerServices; 13 14 namespace Tunnel 15 { 16 public class Ringlogger 17 { 18 private struct UnixTimestamp 19 { 20 private Int64 _ns; 21 public UnixTimestamp(Int64 ns) => _ns = ns; 22 public bool IsEmpty => _ns == 0; 23 public static UnixTimestamp Empty => new UnixTimestamp(0); 24 public static UnixTimestamp Now 25 { 26 get 27 { 28 var now = DateTimeOffset.UtcNow; 29 var ns = (now.Subtract(DateTimeOffset.FromUnixTimeSeconds(0)).Ticks * 100) % 1000000000; 30 return new UnixTimestamp(now.ToUnixTimeSeconds() * 1000000000 + ns); 31 } 32 } 33 public Int64 Nanoseconds => _ns; 34 public override string ToString() 35 { 36 return DateTimeOffset.FromUnixTimeSeconds(_ns / 1000000000).LocalDateTime.ToString("yyyy'-'MM'-'dd HH':'mm':'ss'.'") + ((_ns % 1000000000).ToString() + "00000").Substring(0, 6); 37 } 38 } 39 private struct Line 40 { 41 private const int maxLineLength = 512; 42 private const int offsetTimeNs = 0; 43 private const int offsetLine = 8; 44 45 private readonly MemoryMappedViewAccessor _view; 46 private readonly int _start; 47 public Line(MemoryMappedViewAccessor view, UInt32 index) => (_view, _start) = (view, (int)(Log.HeaderBytes + index * Bytes)); 48 49 public static int Bytes => maxLineLength + offsetLine; 50 51 public UnixTimestamp Timestamp 52 { 53 get => new UnixTimestamp(_view.ReadInt64(_start + offsetTimeNs)); 54 set => _view.Write(_start + offsetTimeNs, value.Nanoseconds); 55 } 56 57 public string Text 58 { 59 get 60 { 61 var textBytes = new byte[maxLineLength]; 62 _view.ReadArray(_start + offsetLine, textBytes, 0, textBytes.Length); 63 var nullByte = Array.IndexOf<byte>(textBytes, 0); 64 if (nullByte <= 0) 65 return null; 66 return Encoding.UTF8.GetString(textBytes, 0, nullByte); 67 } 68 set 69 { 70 if (value == null) 71 { 72 _view.WriteArray(_start + offsetLine, new byte[maxLineLength], 0, maxLineLength); 73 return; 74 } 75 var textBytes = Encoding.UTF8.GetBytes(value); 76 var bytesToWrite = Math.Min(maxLineLength - 1, textBytes.Length); 77 _view.Write(_start + offsetLine + bytesToWrite, (byte)0); 78 _view.WriteArray(_start + offsetLine, textBytes, 0, bytesToWrite); 79 } 80 } 81 82 public override string ToString() 83 { 84 var time = Timestamp; 85 if (time.IsEmpty) 86 return null; 87 var text = Text; 88 if (text == null) 89 return null; 90 return string.Format("{0}: {1}", time, text); 91 } 92 } 93 private struct Log 94 { 95 private const UInt32 maxLines = 2048; 96 private const UInt32 magic = 0xbadbabe; 97 private const int offsetMagic = 0; 98 private const int offsetNextIndex = 4; 99 private const int offsetLines = 8; 100 101 private readonly MemoryMappedViewAccessor _view; 102 public Log(MemoryMappedViewAccessor view) => _view = view; 103 104 public static int HeaderBytes => offsetLines; 105 public static int Bytes => (int)(HeaderBytes + Line.Bytes * maxLines); 106 107 public UInt32 ExpectedMagic => magic; 108 public UInt32 Magic 109 { 110 get => _view.ReadUInt32(offsetMagic); 111 set => _view.Write(offsetMagic, value); 112 } 113 114 public UInt32 NextIndex 115 { 116 get => _view.ReadUInt32(offsetNextIndex); 117 set => _view.Write(offsetNextIndex, value); 118 } 119 public unsafe UInt32 InsertNextIndex() 120 { 121 byte* pointer = null; 122 _view.SafeMemoryMappedViewHandle.AcquirePointer(ref pointer); 123 var ret = (UInt32)Interlocked.Increment(ref Unsafe.AsRef<Int32>(pointer + offsetNextIndex)); 124 _view.SafeMemoryMappedViewHandle.ReleasePointer(); 125 return ret; 126 } 127 128 public UInt32 LineCount => maxLines; 129 public Line this[UInt32 i] => new Line(_view, i % maxLines); 130 131 public void Clear() => _view.WriteArray(0, new byte[Bytes], 0, Bytes); 132 } 133 134 private readonly Log _log; 135 private readonly string _tag; 136 137 public Ringlogger(string filename, string tag) 138 { 139 var file = File.Open(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete); 140 file.SetLength(Log.Bytes); 141 var mmap = MemoryMappedFile.CreateFromFile(file, null, 0, MemoryMappedFileAccess.ReadWrite, HandleInheritability.None, false); 142 var view = mmap.CreateViewAccessor(0, Log.Bytes, MemoryMappedFileAccess.ReadWrite); 143 _log = new Log(view); 144 if (_log.Magic != _log.ExpectedMagic) 145 { 146 _log.Clear(); 147 _log.Magic = _log.ExpectedMagic; 148 } 149 _tag = tag; 150 } 151 152 public void Write(string line) 153 { 154 var time = UnixTimestamp.Now; 155 var entry = _log[_log.InsertNextIndex() - 1]; 156 entry.Timestamp = UnixTimestamp.Empty; 157 entry.Text = null; 158 entry.Text = string.Format("[{0}] {1}", _tag, line.Trim()); 159 entry.Timestamp = time; 160 } 161 162 public void WriteTo(TextWriter writer) 163 { 164 var start = _log.NextIndex; 165 for (UInt32 i = 0; i < _log.LineCount; ++i) 166 { 167 var entry = _log[i + start]; 168 if (entry.Timestamp.IsEmpty) 169 continue; 170 var text = entry.ToString(); 171 if (text == null) 172 continue; 173 writer.WriteLine(text); 174 } 175 } 176 177 public static readonly UInt32 CursorAll = UInt32.MaxValue; 178 public List<string> FollowFromCursor(ref UInt32 cursor) 179 { 180 var lines = new List<string>((int)_log.LineCount); 181 var i = cursor; 182 var all = cursor == CursorAll; 183 if (all) 184 i = _log.NextIndex; 185 for (UInt32 l = 0; l < _log.LineCount; ++l, ++i) 186 { 187 if (!all && i % _log.LineCount == _log.NextIndex % _log.LineCount) 188 break; 189 var entry = _log[i]; 190 if (entry.Timestamp.IsEmpty) 191 { 192 if (all) 193 continue; 194 break; 195 } 196 cursor = (i + 1) % _log.LineCount; 197 var text = entry.ToString(); 198 if (text == null) 199 continue; 200 lines.Add(text); 201 } 202 return lines; 203 } 204 } 205 }