1 //
2 // Copyright (c) 2010-2024 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.Text;
10 using Antmicro.Renode.Logging;
11 using Antmicro.Renode.Peripherals.UART;
12 using Antmicro.Renode.Utilities;
13 using Antmicro.Migrant;
14 using Antmicro.Renode.Core;
15 using Antmicro.Renode.Exceptions;
16 using Antmicro.Renode.Peripherals;
17 using Antmicro.Renode.Time;
18 
19 namespace Antmicro.Renode.Analyzers
20 {
21     // This class is marked as `IExternal` to allow access to
22     // properties from Monitor. In order to do this one must create analyzer
23     // and add it as external using command below:
24     //
25     // showAnalyzer "ExternalName" sysbus.uart_name Antmicro.Renode.Analyzers.LoggingUartAnalyzer
26     //
27     [Transient]
28     public class LoggingUartAnalyzer : BasicPeripheralBackendAnalyzer<UARTBackend>, IExternal
29     {
LoggingUartAnalyzer()30         public LoggingUartAnalyzer()
31         {
32             line = new StringBuilder(InitialCapacity);
33             LogLevel = LogLevel.Info;
34 
35             TimestampFormat = TimestampType.Full;
36         }
37 
AttachTo(UARTBackend backend)38         public override void AttachTo(UARTBackend backend)
39         {
40             base.AttachTo(backend);
41             uart = backend.UART;
42 
43             // let's find out to which machine this uart belongs
44             machine = uart.GetMachine();
45         }
46 
Show()47         public override void Show()
48         {
49             baseStampHost = CustomDateTime.Now;
50             lastLineStampHost = baseStampHost;
51             lastLineStampVirtual = machine.ElapsedVirtualTime.TimeElapsed;
52 
53             uart.CharReceived += WriteChar;
54         }
55 
Hide()56         public override void Hide()
57         {
58             if(!String.IsNullOrWhiteSpace(line.ToString()))
59             {
60                 WriteChar(10); // this allows us to flush the last, unfinished line (e.g. the prompt) to the log
61             }
62             uart.CharReceived -= WriteChar;
63         }
64 
65         public LogLevel LogLevel { get; set; }
66 
67         public TimestampType TimestampFormat { get; set; }
68 
WriteChar(byte value)69         private void WriteChar(byte value)
70         {
71             if(value == 10)
72             {
73                 var hostNow = CustomDateTime.Now;
74                 var virtualNow = machine.LocalTimeSource.ElapsedVirtualTime;
75 
76                 var logLineBuilder = new StringBuilder();
77                 logLineBuilder.Append("[");
78 
79                 var anythingAdded = false;
80                 if(TimestampFormat == TimestampType.Full || TimestampFormat == TimestampType.Host)
81                 {
82                     anythingAdded = true;
83 
84                     var hostTimestamp = string.Format("{0}s (+{1}s)",
85                         Misc.NormalizeDecimal((hostNow - baseStampHost).TotalSeconds),
86                         Misc.NormalizeDecimal((hostNow - lastLineStampHost).TotalSeconds));
87 
88                     if(hostTimestamp.Length < maxHostTimestampLength)
89                     {
90                         hostTimestamp = hostTimestamp.PadLeft(maxHostTimestampLength, ' ');
91                     }
92                     else
93                     {
94                         maxHostTimestampLength = hostTimestamp.Length;
95                     }
96 
97                     logLineBuilder.AppendFormat("host: {0}", hostTimestamp);
98                 }
99 
100                 if(TimestampFormat == TimestampType.Full || TimestampFormat == TimestampType.Virtual)
101                 {
102                     if(anythingAdded)
103                     {
104                         logLineBuilder.Append("|");
105                     }
106                     anythingAdded = true;
107 
108                     var virtTimestamp = string.Format("{0}s (+{1}s)",
109                         Misc.NormalizeDecimal(virtualNow.TotalSeconds),
110                         Misc.NormalizeDecimal((virtualNow - lastLineStampVirtual).TotalSeconds));
111 
112                     if(virtTimestamp.Length < maxVirtTimestampLength)
113                     {
114                         virtTimestamp = virtTimestamp.PadLeft(maxVirtTimestampLength, ' ');
115                     }
116                     else
117                     {
118                         maxVirtTimestampLength = virtTimestamp.Length;
119                     }
120 
121                     logLineBuilder.AppendFormat("virt: {0}", virtTimestamp);
122                 }
123 
124                 if(!anythingAdded)
125                 {
126                     logLineBuilder.Append("output");
127                 }
128 
129                 logLineBuilder.AppendFormat("] {0}", line.ToString());
130 
131                 uart.Log(LogLevel, logLineBuilder.ToString());
132 
133                 lastLineStampHost = hostNow;
134                 lastLineStampVirtual = virtualNow;
135                 line.Clear();
136 
137                 return;
138             }
139             if(char.IsControl((char)value))
140             {
141                 return;
142             }
143 
144             var nextCharacter = (char)value;
145             line.Append(nextCharacter);
146         }
147 
148         private DateTime baseStampHost;
149         private DateTime lastLineStampHost;
150         private TimeInterval lastLineStampVirtual;
151         private IUART uart;
152         private IMachine machine;
153 
154         private int maxHostTimestampLength;
155         private int maxVirtTimestampLength;
156 
157         private readonly StringBuilder line;
158 
159         private const int InitialCapacity = 120;
160 
161         public enum TimestampType
162         {
163             None,
164             Virtual,
165             Host,
166             Full
167         }
168     }
169 }
170 
171