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