1 //
2 // Copyright (c) 2010-2022 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.Runtime.InteropServices;
12 using Antmicro.Renode.Core;
13 using Antmicro.Renode.Exceptions;
14 using Antmicro.Renode.Peripherals.CPU;
15 using Antmicro.Renode.Utilities;
16 
17 namespace Antmicro.Renode.Logging.Profiling
18 {
19     public class Profiler : IDisposable
20     {
Profiler(IMachine machine, string outputPath)21         public Profiler(IMachine machine, string outputPath)
22         {
23             this.machine = machine;
24 
25             try
26             {
27                 output = new FileStream(outputPath, FileMode.Create, FileAccess.ReadWrite, FileShare.Delete);
28             }
29             catch(IOException ex)
30             {
31                 throw new RecoverableException(ex);
32             }
33 
34             EnableProfiling();
35             machine.PeripheralsChanged += OnPeripheralsChanged;
36         }
37 
Log(BaseEntry entry)38         public void Log(BaseEntry entry)
39         {
40             lock(locker)
41             {
42                 if(!headerWritten)
43                 {
44                     WriteHeader();
45                 }
46                 var bytes = Serialize(entry);
47                 output.Write(bytes, 0, bytes.Length);
48             }
49         }
50 
Dispose()51         public void Dispose()
52         {
53             output.Flush();
54             output.Dispose();
55         }
56 
EnableProfiling()57         private void EnableProfiling()
58         {
59             var cpus = machine.GetPeripheralsOfType<ICPUWithMetrics>();
60             foreach(var cpu in cpus)
61             {
62                 cpu.EnableProfiling();
63             }
64         }
65 
OnPeripheralsChanged(IMachine machine, PeripheralsChangedEventArgs args)66         private void OnPeripheralsChanged(IMachine machine, PeripheralsChangedEventArgs args)
67         {
68             if(args.Operation != PeripheralsChangedEventArgs.PeripheralChangeType.Addition)
69             {
70                 return;
71             }
72 
73             var cpu = args.Peripheral as ICPUWithMetrics;
74             if(cpu != null)
75             {
76                 cpu.EnableProfiling();
77             }
78         }
79 
WriteHeader()80         private void WriteHeader()
81         {
82             headerWritten = true;
83             var header = new ProfilerHeader();
84             header.RegisterPeripherals(machine);
85             output.Write(header.Bytes, 0, header.Bytes.Length);
86         }
87 
Serialize(object target)88         private byte[] Serialize(object target)
89         {
90             var size = Marshal.SizeOf(target);
91             var result = new byte[size];
92             var handler = default(GCHandle);
93 
94             try
95             {
96                 handler = GCHandle.Alloc(result, GCHandleType.Pinned);
97                 Marshal.StructureToPtr(target, handler.AddrOfPinnedObject(), false);
98             }
99             finally
100             {
101                 if(handler.IsAllocated)
102                 {
103                     handler.Free();
104                 }
105             }
106 
107             return result;
108         }
109 
110         private bool headerWritten;
111 
112         private readonly static object locker = new object();
113         private readonly FileStream output;
114         private readonly IMachine machine;
115     }
116 }
117