1 //
2 // Copyright (c) 2010-2024 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.Collections.Generic;
8 using System.Globalization;
9 using System.Text;
10 using Antmicro.Renode.Exceptions;
11 
12 namespace Antmicro.Renode.Utilities.GDB
13 {
14     // Based on the extended "thread-id" syntax information from https://sourceware.org/gdb/current/onlinedocs/gdb.html/Packets.html
15     // Shouldn't be used for "those packets and replies explicitly documented to include a process ID, rather than a thread-id".
16     public struct PacketThreadId
17     {
18         /// <param name="gdbArgument">It must be a full argument, i.e., including <c>"p"</c> if it was present.</param>
PacketThreadIdAntmicro.Renode.Utilities.GDB.PacketThreadId19         public PacketThreadId(string gdbArgument)
20         {
21             var ids = gdbArgument.TrimStart('p').Split('.');
22 
23             if(ids.Length == 1 && TryParseId(ids[0], out var id))
24             {
25                 if(gdbArgument.StartsWith('p'))
26                 {
27                     ProcessId = id;
28                     ThreadId = All;
29                 }
30                 else
31                 {
32                     ProcessId = null;
33                     ThreadId = id;
34                 }
35             }
36             else if(ids.Length == 2 && gdbArgument.StartsWith('p')
37                 && TryParseId(ids[0], out var id1) && id1 != All && TryParseId(ids[1], out var id2))
38             {
39                 ProcessId = id1;
40                 ThreadId = id2;
41             }
42             else
43             {
44                 throw new RecoverableException($"Invalid GDB packet's thread-id argument: {gdbArgument}");
45             }
46         }
47 
ToStringAntmicro.Renode.Utilities.GDB.PacketThreadId48         public override string ToString()
49         {
50             return string.Empty
51                 .AppendIf(ProcessId.HasValue, $"process-id: {IdToString(ProcessId.Value)}, ")
52                 .Append($"thread-id: {IdToString(ThreadId)}").ToString();
53         }
54 
55         public int? ProcessId;
56         public int ThreadId;
57 
58         // All and Any can be passed as a part of a valid argument.
59         public const int All = -1;
60         public const int Any = 0;
61 
IdToStringAntmicro.Renode.Utilities.GDB.PacketThreadId62         private static string IdToString(int id)
63         {
64             switch(id)
65             {
66                 case All:
67                     return "all";
68                 case Any:
69                     return "any";
70                 default:
71                     return id.ToString();
72             }
73         }
74 
TryParseIdAntmicro.Renode.Utilities.GDB.PacketThreadId75         private static bool TryParseId(string s, out int result)
76         {
77             if(s == "-1")
78             {
79                 result = -1;
80                 return true;
81             }
82             else
83             {
84                 // No need to ensure it isn't lower than -1 because negative values aren't allowed with HexNumber.
85                 return int.TryParse(s, NumberStyles.HexNumber, NumberFormatInfo.InvariantInfo, out result);
86             }
87         }
88     }
89 }
90 
91