1 // 2 // Copyright (c) 2010-2018 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System; 9 using System.IO; 10 using Antmicro.Migrant; 11 using Antmicro.Migrant.Customization; 12 using Antmicro.Renode.Peripherals; 13 using Antmicro.Renode.Core; 14 using Antmicro.Renode.Logging; 15 using System.Collections.Generic; 16 using Antmicro.Renode.Time; 17 18 namespace Antmicro.Renode.EventRecording 19 { 20 [Transient] 21 public sealed class Recorder : IDisposable 22 { Recorder(FileStream stream, IMachine machine, RecordingBehaviour recordingBehaviour)23 public Recorder(FileStream stream, IMachine machine, RecordingBehaviour recordingBehaviour) 24 { 25 this.stream = stream; 26 this.machine = machine; 27 this.recordingBehaviour = recordingBehaviour; 28 nullifiedHandlersCache = new Dictionary<Delegate, Delegate>(); 29 openStreamSerializer = new Serializer(new Settings(useBuffering: false, disableTypeStamping: true)).ObtainOpenStreamSerializer(stream); 30 } 31 Record(T value, Action<T> handler, TimeInterval timestamp, bool domainExternal)32 public void Record<T>(T value, Action<T> handler, TimeInterval timestamp, bool domainExternal) 33 { 34 string name; 35 if(!TryExtractName(handler, out name)) 36 { 37 return; 38 } 39 var recordEntry = new RecordEntry<T>(name, value, GetNullifiedHandler<Action<T>>(handler), timestamp); 40 RecordInner(recordEntry, domainExternal); 41 } 42 Record(T1 value1, T2 value2, Action<T1, T2> handler, TimeInterval timestamp, bool domainExternal)43 public void Record<T1, T2>(T1 value1, T2 value2, Action<T1, T2> handler, TimeInterval timestamp, bool domainExternal) 44 { 45 string name; 46 if(!TryExtractName(handler, out name)) 47 { 48 return; 49 } 50 var recordEntry = new RecordEntry<T1, T2>(name, value1, value2, GetNullifiedHandler<Action<T1, T2>>(handler), timestamp); 51 RecordInner(recordEntry, domainExternal); 52 } 53 Dispose()54 public void Dispose() 55 { 56 stream.Dispose(); 57 } 58 TryExtractName(Delegate handler, out string name)59 private bool TryExtractName(Delegate handler, out string name) 60 { 61 name = string.Empty; 62 var peripheral = handler.Target as IPeripheral; 63 if(peripheral == null || !machine.TryGetAnyName(peripheral, out name)) 64 { 65 machine.Log(LogLevel.Warning, "Record request by a non-peripheral or not named peripheral. Ignored."); 66 return false; 67 } 68 return true; 69 } 70 RecordInner(IRecordEntry recordEntry, bool domainExternal)71 private void RecordInner(IRecordEntry recordEntry, bool domainExternal) 72 { 73 if(!domainExternal && recordingBehaviour == RecordingBehaviour.DomainExternal) 74 { 75 return; 76 } 77 openStreamSerializer.Serialize(recordEntry); 78 } 79 GetNullifiedHandler(Delegate handler)80 private T GetNullifiedHandler<T>(Delegate handler) 81 { 82 // this function should ideally have something like where T : Delegate 83 // but it is not possible; this is why we cast through object 84 // actually, the type expected as T are Action<?, ...> 85 86 // we remove the target from the delegate - we don't want to serialize the peripheral 87 // (it can be later get by its name), but we definitely would like to save MethodInfo 88 //handler = GetNullifiedHandler(handler); 89 if(nullifiedHandlersCache.ContainsKey(handler)) 90 { 91 return (T)(object)nullifiedHandlersCache[handler]; 92 } 93 var result = (T)(object)Delegate.CreateDelegate(typeof(T), null, handler.Method); 94 nullifiedHandlersCache.Add(handler, (Delegate)(object)result); 95 return result; 96 } 97 98 private readonly Serializer.OpenStreamSerializer openStreamSerializer; 99 private readonly Dictionary<Delegate, Delegate> nullifiedHandlersCache; 100 private readonly RecordingBehaviour recordingBehaviour; 101 private readonly FileStream stream; 102 private readonly IMachine machine; 103 } 104 } 105 106