1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using System; 8 using System.IO; 9 using System.Linq; 10 using System.Collections.Generic; 11 using Antmicro.Renode.Exceptions; 12 using Antmicro.Renode.Time; 13 using Antmicro.Renode.Core; 14 15 namespace Antmicro.Renode.Utilities 16 { 17 public class SnapshotTracker : IExternal 18 { SnapshotTracker()19 public SnapshotTracker() 20 { 21 snapshots = new List<SnapshotDescriptor>(); 22 snapshotComparer = new SnapshotComparer(); 23 } 24 GetLastSnapshotBeforeOrAtTimeStamp(TimeInterval timeStamp)25 public string GetLastSnapshotBeforeOrAtTimeStamp(TimeInterval timeStamp) 26 { 27 int index = snapshots.BinarySearch(new SnapshotDescriptor(timeStamp, null), snapshotComparer); 28 29 if(index == -1) 30 { 31 throw new RecoverableException("There are no snapshots taken before this timestamp."); 32 } 33 34 if(index < 0) 35 { 36 // binary search returned bitwise complement of the index of the least element with larger timestamp 37 // we decrement index to get the largest element less than given one 38 index = ~index; 39 index--; 40 } 41 42 while(index >= 0 && !File.Exists(snapshots[index].Path)) 43 { 44 snapshots.RemoveAt(index); 45 index--; 46 } 47 48 if(index >= 0) 49 { 50 return snapshots[index].Path; 51 } 52 53 throw new RecoverableException("There are no snapshots taken before this timestamp."); 54 } 55 Save(TimeInterval timeStamp, string path)56 public void Save(TimeInterval timeStamp, string path) 57 { 58 var newSnap = new SnapshotDescriptor(timeStamp, path); 59 60 int index = snapshots.BinarySearch(newSnap, snapshotComparer); 61 62 // only save snapshot if there is no other with the same timestamp to keep 63 // the oldest snapshot at given virtual time in order to cover more breakpoints 64 if(index < 0) 65 { 66 // binary search returned bitwise complement of the index of the first element larger than the new one 67 snapshots.Insert(~index, newSnap); 68 } 69 } 70 PrintSnapshotsInfo()71 public string PrintSnapshotsInfo() 72 { 73 return $"Count: {Count}\nTotal Size: {GetSnapshotSizeText(TotalSnapshotsSize)}"; 74 } 75 PrintDetailedSnapshotsInfo()76 public string[,] PrintDetailedSnapshotsInfo() 77 { 78 var table = new Table().AddRow("Path", "Timestamp", "Size"); 79 table.AddRows(snapshots, 80 x => x.Path, 81 x => x.TimeStamp.ToString(), 82 x => GetSnapshotSizeText(new FileInfo(x.Path).Length) 83 ); 84 85 return table.ToArray(); 86 } 87 88 public int Count => snapshots.Count; 89 90 public long TotalSnapshotsSize => snapshots.Select(x => new FileInfo(x.Path)).Where(x => x.Exists).Sum(x => x.Length); 91 GetSnapshotSizeText(long size)92 private string GetSnapshotSizeText(long size) 93 { 94 Misc.CalculateUnitSuffix(size, out var value, out var unit); 95 return $"{value:F2} {unit}"; 96 } 97 98 private readonly List<SnapshotDescriptor> snapshots; 99 private readonly SnapshotComparer snapshotComparer; 100 101 private class SnapshotDescriptor 102 { SnapshotDescriptor(TimeInterval timeStamp, string path)103 public SnapshotDescriptor(TimeInterval timeStamp, string path) 104 { 105 TimeStamp = timeStamp; 106 Path = path; 107 } 108 109 public TimeInterval TimeStamp { get; } 110 public string Path { get; } 111 } 112 113 private class SnapshotComparer : IComparer<SnapshotDescriptor> 114 { Compare(SnapshotDescriptor snap1, SnapshotDescriptor snap2)115 public int Compare(SnapshotDescriptor snap1, SnapshotDescriptor snap2) 116 { 117 return snap1.TimeStamp.CompareTo(snap2.TimeStamp); 118 } 119 } 120 } 121 } 122 123