1 //
2 // Copyright (c) 2010-2023 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.Collections.Generic;
9 using System.IO;
10 using System.Linq;
11 using System.Text;
12 using Antmicro.Renode.Debugging;
13 using Antmicro.Renode.Exceptions;
14 using Antmicro.Renode.Logging;
15 using Antmicro.Renode.Peripherals.Bus;
16 using Antmicro.Renode.Utilities.Binding;
17 
18 namespace Antmicro.Renode.Peripherals.CPU.GuestProfiling.ProtoBuf
19 {
20     public class PerfettoTraceWriter
21     {
PerfettoTraceWriter()22         public PerfettoTraceWriter()
23         {
24             trace = new Trace();
25         }
26 
CreateTrack(string name, ulong trackId, bool isCounterTrack = false)27         public void CreateTrack(string name, ulong trackId, bool isCounterTrack = false)
28         {
29             var track = new TrackDescriptor
30             {
31                 Uuid = trackId,
32                 Name = name
33             };
34 
35             if(isCounterTrack)
36             {
37                 track.Counter = new TrackDescriptor.CounterDescriptor();
38             }
39 
40             trace.Packets.Add(new TracePacket
41             {
42                 TrustedPacketSequenceId = SequenceId,
43                 TrackDescriptor = track
44             });
45         }
46 
CreateEventBegin(ulong timestamp, string name, ulong trackId)47         public void CreateEventBegin(ulong timestamp, string name, ulong trackId)
48         {
49             trace.Packets.Add(new TracePacket
50             {
51                 Timestamp = timestamp,
52                 TrustedPacketSequenceId = SequenceId,
53                 TrackEvent = new TrackEvent
54                 {
55                     Name = name,
56                     TrackUuid = trackId,
57                     type = TrackEvent.Type.TypeSliceBegin
58                 }
59             });
60         }
61 
CreateEventEnd(ulong timestamp, ulong trackId)62         public void CreateEventEnd(ulong timestamp, ulong trackId)
63         {
64             trace.Packets.Add(new TracePacket
65             {
66                 Timestamp = timestamp,
67                 TrustedPacketSequenceId = SequenceId,
68                 TrackEvent = new TrackEvent
69                 {
70                     TrackUuid = trackId,
71                     type = TrackEvent.Type.TypeSliceEnd
72                 }
73             });
74         }
75 
CreateEventInstant(ulong timestamp, string name, ulong trackId)76         public void CreateEventInstant(ulong timestamp, string name, ulong trackId)
77         {
78             trace.Packets.Add(new TracePacket
79             {
80                 Timestamp = timestamp,
81                 TrustedPacketSequenceId = SequenceId,
82                 TrackEvent = new TrackEvent
83                 {
84                     Name = name,
85                     TrackUuid = trackId,
86                     type = TrackEvent.Type.TypeInstant
87                 }
88             });
89         }
90 
CreateEventCounter(ulong timestamp, long value, ulong trackId)91         public void CreateEventCounter(ulong timestamp, long value, ulong trackId)
92         {
93             trace.Packets.Add(new TracePacket
94             {
95                 Timestamp = timestamp,
96                 TrustedPacketSequenceId = SequenceId,
97                 TrackEvent = new TrackEvent
98                 {
99                     CounterValue = value,
100                     TrackUuid = trackId,
101                     type = TrackEvent.Type.TypeCounter
102                 }
103             });
104         }
105 
CreateEventCounter(ulong timestamp, double value, ulong trackId)106         public void CreateEventCounter(ulong timestamp, double value, ulong trackId)
107         {
108             trace.Packets.Add(new TracePacket
109             {
110                 Timestamp = timestamp,
111                 TrustedPacketSequenceId = SequenceId,
112                 TrackEvent = new TrackEvent
113                 {
114                     DoubleCounterValue = value,
115                     TrackUuid = trackId,
116                     type = TrackEvent.Type.TypeCounter
117                 }
118             });
119         }
120 
FlushBuffer(Stream fileStream)121         public void FlushBuffer(Stream fileStream)
122         {
123             if(lastSyncMarker >= SyncMarkerInterval)
124             {
125                 // Add a sync packet
126                 trace.Packets.Add(new TracePacket
127                 {
128                     TrustedPacketSequenceId = SequenceId,
129                     SynchronizationMarker = SyncMarkerBytes
130                 });
131                 lastSyncMarker = 0;
132             }
133 
134             global::ProtoBuf.Serializer.Serialize(fileStream, trace);
135             lastSyncMarker += (uint)PacketCount;
136             trace.Packets.Clear();
137         }
138 
RemoveLastNPackets(int packetCount)139         public void RemoveLastNPackets(int packetCount)
140         {
141             trace.Packets.RemoveRange(trace.Packets.Count - packetCount, packetCount);
142         }
143 
144         public int PacketCount => trace.Packets.Count;
145 
146         // Perfetto will look for this exact sequence when it is parsing a longer trace
147         // Emitting this sequence can be used to partition the trace, so Perfetto doesn't have to load everything at once
148         private static readonly byte[] SyncMarkerBytes = Guid.ParseExact("{82477a76-b28d-42ba-81dc-33326d57a079}", "B").ToByteArray();
149 
150         private readonly Trace trace;
151         private uint lastSyncMarker;
152 
153         private const uint SequenceId = 0;
154         private const uint SyncMarkerInterval = 10000;
155     }
156 }
157