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