1 //===-- StringExtractorGDBRemote.cpp --------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Utility/StringExtractorGDBRemote.h"
10 
11 #include <cctype>
12 #include <cstring>
13 #include <optional>
14 
15 constexpr lldb::pid_t StringExtractorGDBRemote::AllProcesses;
16 constexpr lldb::tid_t StringExtractorGDBRemote::AllThreads;
17 
18 StringExtractorGDBRemote::ResponseType
GetResponseType() const19 StringExtractorGDBRemote::GetResponseType() const {
20   if (m_packet.empty())
21     return eUnsupported;
22 
23   switch (m_packet[0]) {
24   case 'E':
25     if (isxdigit(m_packet[1]) && isxdigit(m_packet[2])) {
26       if (m_packet.size() == 3)
27         return eError;
28       llvm::StringRef packet_ref(m_packet);
29       if (packet_ref[3] == ';') {
30         auto err_string = packet_ref.substr(4);
31         for (auto e : err_string)
32           if (!isxdigit(e))
33             return eResponse;
34         return eError;
35       }
36     }
37     break;
38 
39   case 'O':
40     if (m_packet.size() == 2 && m_packet[1] == 'K')
41       return eOK;
42     break;
43 
44   case '+':
45     if (m_packet.size() == 1)
46       return eAck;
47     break;
48 
49   case '-':
50     if (m_packet.size() == 1)
51       return eNack;
52     break;
53   }
54   return eResponse;
55 }
56 
57 StringExtractorGDBRemote::ServerPacketType
GetServerPacketType() const58 StringExtractorGDBRemote::GetServerPacketType() const {
59 #define PACKET_MATCHES(s)                                                      \
60   ((packet_size == (sizeof(s) - 1)) && (strcmp((packet_cstr), (s)) == 0))
61 #define PACKET_STARTS_WITH(s)                                                  \
62   ((packet_size >= (sizeof(s) - 1)) &&                                         \
63    ::strncmp(packet_cstr, s, (sizeof(s) - 1)) == 0)
64 
65   // Empty is not a supported packet...
66   if (m_packet.empty())
67     return eServerPacketType_invalid;
68 
69   const size_t packet_size = m_packet.size();
70   const char *packet_cstr = m_packet.c_str();
71   switch (m_packet[0]) {
72 
73   case '%':
74     return eServerPacketType_notify;
75 
76   case '\x03':
77     if (packet_size == 1)
78       return eServerPacketType_interrupt;
79     break;
80 
81   case '-':
82     if (packet_size == 1)
83       return eServerPacketType_nack;
84     break;
85 
86   case '+':
87     if (packet_size == 1)
88       return eServerPacketType_ack;
89     break;
90 
91   case 'A':
92     return eServerPacketType_A;
93 
94   case 'Q':
95 
96     switch (packet_cstr[1]) {
97     case 'E':
98       if (PACKET_STARTS_WITH("QEnvironment:"))
99         return eServerPacketType_QEnvironment;
100       if (PACKET_STARTS_WITH("QEnvironmentHexEncoded:"))
101         return eServerPacketType_QEnvironmentHexEncoded;
102       if (PACKET_STARTS_WITH("QEnableErrorStrings"))
103         return eServerPacketType_QEnableErrorStrings;
104       break;
105 
106     case 'P':
107       if (PACKET_STARTS_WITH("QPassSignals:"))
108         return eServerPacketType_QPassSignals;
109       break;
110 
111     case 'S':
112       if (PACKET_MATCHES("QStartNoAckMode"))
113         return eServerPacketType_QStartNoAckMode;
114       if (PACKET_STARTS_WITH("QSaveRegisterState"))
115         return eServerPacketType_QSaveRegisterState;
116       if (PACKET_STARTS_WITH("QSetDisableASLR:"))
117         return eServerPacketType_QSetDisableASLR;
118       if (PACKET_STARTS_WITH("QSetDetachOnError:"))
119         return eServerPacketType_QSetDetachOnError;
120       if (PACKET_STARTS_WITH("QSetSTDIN:"))
121         return eServerPacketType_QSetSTDIN;
122       if (PACKET_STARTS_WITH("QSetSTDOUT:"))
123         return eServerPacketType_QSetSTDOUT;
124       if (PACKET_STARTS_WITH("QSetSTDERR:"))
125         return eServerPacketType_QSetSTDERR;
126       if (PACKET_STARTS_WITH("QSetWorkingDir:"))
127         return eServerPacketType_QSetWorkingDir;
128       if (PACKET_STARTS_WITH("QSetLogging:"))
129         return eServerPacketType_QSetLogging;
130       if (PACKET_STARTS_WITH("QSetIgnoredExceptions"))
131         return eServerPacketType_QSetIgnoredExceptions;
132       if (PACKET_STARTS_WITH("QSetMaxPacketSize:"))
133         return eServerPacketType_QSetMaxPacketSize;
134       if (PACKET_STARTS_WITH("QSetMaxPayloadSize:"))
135         return eServerPacketType_QSetMaxPayloadSize;
136       if (PACKET_STARTS_WITH("QSetEnableAsyncProfiling;"))
137         return eServerPacketType_QSetEnableAsyncProfiling;
138       if (PACKET_STARTS_WITH("QSyncThreadState:"))
139         return eServerPacketType_QSyncThreadState;
140       break;
141 
142     case 'L':
143       if (PACKET_STARTS_WITH("QLaunchArch:"))
144         return eServerPacketType_QLaunchArch;
145       if (PACKET_MATCHES("QListThreadsInStopReply"))
146         return eServerPacketType_QListThreadsInStopReply;
147       break;
148 
149     case 'M':
150       if (PACKET_STARTS_WITH("QMemTags"))
151         return eServerPacketType_QMemTags;
152       break;
153 
154     case 'N':
155       if (PACKET_STARTS_WITH("QNonStop:"))
156         return eServerPacketType_QNonStop;
157       break;
158 
159     case 'R':
160       if (PACKET_STARTS_WITH("QRestoreRegisterState:"))
161         return eServerPacketType_QRestoreRegisterState;
162       break;
163 
164     case 'T':
165       if (PACKET_MATCHES("QThreadSuffixSupported"))
166         return eServerPacketType_QThreadSuffixSupported;
167       break;
168     }
169     break;
170 
171   case 'q':
172     switch (packet_cstr[1]) {
173     case 's':
174       if (PACKET_MATCHES("qsProcessInfo"))
175         return eServerPacketType_qsProcessInfo;
176       if (PACKET_MATCHES("qsThreadInfo"))
177         return eServerPacketType_qsThreadInfo;
178       break;
179 
180     case 'f':
181       if (PACKET_STARTS_WITH("qfProcessInfo"))
182         return eServerPacketType_qfProcessInfo;
183       if (PACKET_STARTS_WITH("qfThreadInfo"))
184         return eServerPacketType_qfThreadInfo;
185       break;
186 
187     case 'C':
188       if (packet_size == 2)
189         return eServerPacketType_qC;
190       break;
191 
192     case 'E':
193       if (PACKET_STARTS_WITH("qEcho:"))
194         return eServerPacketType_qEcho;
195       break;
196 
197     case 'F':
198       if (PACKET_STARTS_WITH("qFileLoadAddress:"))
199         return eServerPacketType_qFileLoadAddress;
200       break;
201 
202     case 'G':
203       if (PACKET_STARTS_WITH("qGroupName:"))
204         return eServerPacketType_qGroupName;
205       if (PACKET_MATCHES("qGetWorkingDir"))
206         return eServerPacketType_qGetWorkingDir;
207       if (PACKET_MATCHES("qGetPid"))
208         return eServerPacketType_qGetPid;
209       if (PACKET_STARTS_WITH("qGetProfileData;"))
210         return eServerPacketType_qGetProfileData;
211       if (PACKET_MATCHES("qGDBServerVersion"))
212         return eServerPacketType_qGDBServerVersion;
213       break;
214 
215     case 'H':
216       if (PACKET_MATCHES("qHostInfo"))
217         return eServerPacketType_qHostInfo;
218       break;
219 
220     case 'K':
221       if (PACKET_STARTS_WITH("qKillSpawnedProcess"))
222         return eServerPacketType_qKillSpawnedProcess;
223       break;
224 
225     case 'L':
226       if (PACKET_STARTS_WITH("qLaunchGDBServer"))
227         return eServerPacketType_qLaunchGDBServer;
228       if (PACKET_MATCHES("qLaunchSuccess"))
229         return eServerPacketType_qLaunchSuccess;
230       break;
231 
232     case 'M':
233       if (PACKET_STARTS_WITH("qMemoryRegionInfo:"))
234         return eServerPacketType_qMemoryRegionInfo;
235       if (PACKET_MATCHES("qMemoryRegionInfo"))
236         return eServerPacketType_qMemoryRegionInfoSupported;
237       if (PACKET_STARTS_WITH("qModuleInfo:"))
238         return eServerPacketType_qModuleInfo;
239       if (PACKET_STARTS_WITH("qMemTags:"))
240         return eServerPacketType_qMemTags;
241       break;
242 
243     case 'P':
244       if (PACKET_STARTS_WITH("qProcessInfoPID:"))
245         return eServerPacketType_qProcessInfoPID;
246       if (PACKET_STARTS_WITH("qPlatform_shell:"))
247         return eServerPacketType_qPlatform_shell;
248       if (PACKET_STARTS_WITH("qPlatform_mkdir:"))
249         return eServerPacketType_qPlatform_mkdir;
250       if (PACKET_STARTS_WITH("qPlatform_chmod:"))
251         return eServerPacketType_qPlatform_chmod;
252       if (PACKET_MATCHES("qProcessInfo"))
253         return eServerPacketType_qProcessInfo;
254       if (PACKET_STARTS_WITH("qPathComplete:"))
255         return eServerPacketType_qPathComplete;
256       break;
257 
258     case 'Q':
259       if (PACKET_MATCHES("qQueryGDBServer"))
260         return eServerPacketType_qQueryGDBServer;
261       break;
262 
263     case 'R':
264       if (PACKET_STARTS_WITH("qRcmd,"))
265         return eServerPacketType_qRcmd;
266       if (PACKET_STARTS_WITH("qRegisterInfo"))
267         return eServerPacketType_qRegisterInfo;
268       break;
269 
270     case 'S':
271       if (PACKET_STARTS_WITH("qSaveCore"))
272         return eServerPacketType_qLLDBSaveCore;
273       if (PACKET_STARTS_WITH("qSpeedTest:"))
274         return eServerPacketType_qSpeedTest;
275       if (PACKET_MATCHES("qShlibInfoAddr"))
276         return eServerPacketType_qShlibInfoAddr;
277       if (PACKET_MATCHES("qStepPacketSupported"))
278         return eServerPacketType_qStepPacketSupported;
279       if (PACKET_STARTS_WITH("qSupported"))
280         return eServerPacketType_qSupported;
281       if (PACKET_MATCHES("qSyncThreadStateSupported"))
282         return eServerPacketType_qSyncThreadStateSupported;
283       break;
284 
285     case 'T':
286       if (PACKET_STARTS_WITH("qThreadExtraInfo,"))
287         return eServerPacketType_qThreadExtraInfo;
288       if (PACKET_STARTS_WITH("qThreadStopInfo"))
289         return eServerPacketType_qThreadStopInfo;
290       break;
291 
292     case 'U':
293       if (PACKET_STARTS_WITH("qUserName:"))
294         return eServerPacketType_qUserName;
295       break;
296 
297     case 'V':
298       if (PACKET_MATCHES("qVAttachOrWaitSupported"))
299         return eServerPacketType_qVAttachOrWaitSupported;
300       break;
301 
302     case 'W':
303       if (PACKET_STARTS_WITH("qWatchpointSupportInfo:"))
304         return eServerPacketType_qWatchpointSupportInfo;
305       if (PACKET_MATCHES("qWatchpointSupportInfo"))
306         return eServerPacketType_qWatchpointSupportInfoSupported;
307       break;
308 
309     case 'X':
310       if (PACKET_STARTS_WITH("qXfer:"))
311         return eServerPacketType_qXfer;
312       break;
313     }
314     break;
315 
316   case 'j':
317     if (PACKET_STARTS_WITH("jModulesInfo:"))
318       return eServerPacketType_jModulesInfo;
319     if (PACKET_MATCHES("jSignalsInfo"))
320       return eServerPacketType_jSignalsInfo;
321     if (PACKET_MATCHES("jThreadsInfo"))
322       return eServerPacketType_jThreadsInfo;
323 
324     if (PACKET_MATCHES("jLLDBTraceSupported"))
325       return eServerPacketType_jLLDBTraceSupported;
326     if (PACKET_STARTS_WITH("jLLDBTraceStop:"))
327       return eServerPacketType_jLLDBTraceStop;
328     if (PACKET_STARTS_WITH("jLLDBTraceStart:"))
329       return eServerPacketType_jLLDBTraceStart;
330     if (PACKET_STARTS_WITH("jLLDBTraceGetState:"))
331       return eServerPacketType_jLLDBTraceGetState;
332     if (PACKET_STARTS_WITH("jLLDBTraceGetBinaryData:"))
333       return eServerPacketType_jLLDBTraceGetBinaryData;
334     break;
335 
336   case 'v':
337     if (PACKET_STARTS_WITH("vFile:")) {
338       if (PACKET_STARTS_WITH("vFile:open:"))
339         return eServerPacketType_vFile_open;
340       else if (PACKET_STARTS_WITH("vFile:close:"))
341         return eServerPacketType_vFile_close;
342       else if (PACKET_STARTS_WITH("vFile:pread"))
343         return eServerPacketType_vFile_pread;
344       else if (PACKET_STARTS_WITH("vFile:pwrite"))
345         return eServerPacketType_vFile_pwrite;
346       else if (PACKET_STARTS_WITH("vFile:size"))
347         return eServerPacketType_vFile_size;
348       else if (PACKET_STARTS_WITH("vFile:exists"))
349         return eServerPacketType_vFile_exists;
350       else if (PACKET_STARTS_WITH("vFile:fstat"))
351         return eServerPacketType_vFile_fstat;
352       else if (PACKET_STARTS_WITH("vFile:stat"))
353         return eServerPacketType_vFile_stat;
354       else if (PACKET_STARTS_WITH("vFile:mode"))
355         return eServerPacketType_vFile_mode;
356       else if (PACKET_STARTS_WITH("vFile:MD5"))
357         return eServerPacketType_vFile_md5;
358       else if (PACKET_STARTS_WITH("vFile:symlink"))
359         return eServerPacketType_vFile_symlink;
360       else if (PACKET_STARTS_WITH("vFile:unlink"))
361         return eServerPacketType_vFile_unlink;
362 
363     } else {
364       if (PACKET_STARTS_WITH("vAttach;"))
365         return eServerPacketType_vAttach;
366       if (PACKET_STARTS_WITH("vAttachWait;"))
367         return eServerPacketType_vAttachWait;
368       if (PACKET_STARTS_WITH("vAttachOrWait;"))
369         return eServerPacketType_vAttachOrWait;
370       if (PACKET_STARTS_WITH("vAttachName;"))
371         return eServerPacketType_vAttachName;
372       if (PACKET_STARTS_WITH("vCont;"))
373         return eServerPacketType_vCont;
374       if (PACKET_MATCHES("vCont?"))
375         return eServerPacketType_vCont_actions;
376       if (PACKET_STARTS_WITH("vKill;"))
377         return eServerPacketType_vKill;
378       if (PACKET_STARTS_WITH("vRun;"))
379         return eServerPacketType_vRun;
380       if (PACKET_MATCHES("vStopped"))
381         return eServerPacketType_vStopped;
382       if (PACKET_MATCHES("vCtrlC"))
383         return eServerPacketType_vCtrlC;
384       if (PACKET_MATCHES("vStdio"))
385         return eServerPacketType_vStdio;
386       break;
387 
388     }
389     break;
390   case '_':
391     switch (packet_cstr[1]) {
392     case 'M':
393       return eServerPacketType__M;
394 
395     case 'm':
396       return eServerPacketType__m;
397     }
398     break;
399 
400   case '?':
401     if (packet_size == 1)
402       return eServerPacketType_stop_reason;
403     break;
404 
405   case 'c':
406     return eServerPacketType_c;
407 
408   case 'C':
409     return eServerPacketType_C;
410 
411   case 'D':
412     return eServerPacketType_D;
413 
414   case 'g':
415     return eServerPacketType_g;
416 
417   case 'G':
418     return eServerPacketType_G;
419 
420   case 'H':
421     return eServerPacketType_H;
422 
423   case 'I':
424     return eServerPacketType_I;
425 
426   case 'k':
427     if (packet_size == 1)
428       return eServerPacketType_k;
429     break;
430 
431   case 'm':
432     return eServerPacketType_m;
433 
434   case 'M':
435     return eServerPacketType_M;
436 
437   case 'p':
438     return eServerPacketType_p;
439 
440   case 'P':
441     return eServerPacketType_P;
442 
443   case 's':
444     if (packet_size == 1)
445       return eServerPacketType_s;
446     break;
447 
448   case 'S':
449     return eServerPacketType_S;
450 
451   case 'x':
452     return eServerPacketType_x;
453 
454   case 'X':
455     return eServerPacketType_X;
456 
457   case 'T':
458     return eServerPacketType_T;
459 
460   case 'z':
461     if (packet_cstr[1] >= '0' && packet_cstr[1] <= '4')
462       return eServerPacketType_z;
463     break;
464 
465   case 'Z':
466     if (packet_cstr[1] >= '0' && packet_cstr[1] <= '4')
467       return eServerPacketType_Z;
468     break;
469   }
470   return eServerPacketType_unimplemented;
471 }
472 
IsOKResponse() const473 bool StringExtractorGDBRemote::IsOKResponse() const {
474   return GetResponseType() == eOK;
475 }
476 
IsUnsupportedResponse() const477 bool StringExtractorGDBRemote::IsUnsupportedResponse() const {
478   return GetResponseType() == eUnsupported;
479 }
480 
IsNormalResponse() const481 bool StringExtractorGDBRemote::IsNormalResponse() const {
482   return GetResponseType() == eResponse;
483 }
484 
IsErrorResponse() const485 bool StringExtractorGDBRemote::IsErrorResponse() const {
486   return GetResponseType() == eError && isxdigit(m_packet[1]) &&
487          isxdigit(m_packet[2]);
488 }
489 
GetError()490 uint8_t StringExtractorGDBRemote::GetError() {
491   if (GetResponseType() == eError) {
492     SetFilePos(1);
493     return GetHexU8(255);
494   }
495   return 0;
496 }
497 
GetStatus()498 lldb_private::Status StringExtractorGDBRemote::GetStatus() {
499   lldb_private::Status error;
500   if (GetResponseType() == eError) {
501     SetFilePos(1);
502     uint8_t errc = GetHexU8(255);
503     error.SetError(errc, lldb::eErrorTypeGeneric);
504 
505     error.SetErrorStringWithFormat("Error %u", errc);
506     std::string error_messg;
507     if (GetChar() == ';') {
508       GetHexByteString(error_messg);
509       error.SetErrorString(error_messg);
510     }
511   }
512   return error;
513 }
514 
GetEscapedBinaryData(std::string & str)515 size_t StringExtractorGDBRemote::GetEscapedBinaryData(std::string &str) {
516   // Just get the data bytes in the string as
517   // GDBRemoteCommunication::CheckForPacket() already removes any 0x7d escaped
518   // characters. If any 0x7d characters are left in the packet, then they are
519   // supposed to be there...
520   str.clear();
521   const size_t bytes_left = GetBytesLeft();
522   if (bytes_left > 0) {
523     str.assign(m_packet, m_index, bytes_left);
524     m_index += bytes_left;
525   }
526   return str.size();
527 }
528 
529 static bool
OKErrorNotSupportedResponseValidator(void *,const StringExtractorGDBRemote & response)530 OKErrorNotSupportedResponseValidator(void *,
531                                      const StringExtractorGDBRemote &response) {
532   switch (response.GetResponseType()) {
533   case StringExtractorGDBRemote::eOK:
534   case StringExtractorGDBRemote::eError:
535   case StringExtractorGDBRemote::eUnsupported:
536     return true;
537 
538   case StringExtractorGDBRemote::eAck:
539   case StringExtractorGDBRemote::eNack:
540   case StringExtractorGDBRemote::eResponse:
541     break;
542   }
543   return false;
544 }
545 
JSONResponseValidator(void *,const StringExtractorGDBRemote & response)546 static bool JSONResponseValidator(void *,
547                                   const StringExtractorGDBRemote &response) {
548   switch (response.GetResponseType()) {
549   case StringExtractorGDBRemote::eUnsupported:
550   case StringExtractorGDBRemote::eError:
551     return true; // Accept unsupported or EXX as valid responses
552 
553   case StringExtractorGDBRemote::eOK:
554   case StringExtractorGDBRemote::eAck:
555   case StringExtractorGDBRemote::eNack:
556     break;
557 
558   case StringExtractorGDBRemote::eResponse:
559     // JSON that is returned in from JSON query packets is currently always
560     // either a dictionary which starts with a '{', or an array which starts
561     // with a '['. This is a quick validator to just make sure the response
562     // could be valid JSON without having to validate all of the
563     // JSON content.
564     switch (response.GetStringRef()[0]) {
565     case '{':
566       return true;
567     case '[':
568       return true;
569     default:
570       break;
571     }
572     break;
573   }
574   return false;
575 }
576 
577 static bool
ASCIIHexBytesResponseValidator(void *,const StringExtractorGDBRemote & response)578 ASCIIHexBytesResponseValidator(void *,
579                                const StringExtractorGDBRemote &response) {
580   switch (response.GetResponseType()) {
581   case StringExtractorGDBRemote::eUnsupported:
582   case StringExtractorGDBRemote::eError:
583     return true; // Accept unsupported or EXX as valid responses
584 
585   case StringExtractorGDBRemote::eOK:
586   case StringExtractorGDBRemote::eAck:
587   case StringExtractorGDBRemote::eNack:
588     break;
589 
590   case StringExtractorGDBRemote::eResponse: {
591     uint32_t valid_count = 0;
592     for (const char ch : response.GetStringRef()) {
593       if (!isxdigit(ch)) {
594         return false;
595       }
596       if (++valid_count >= 16)
597         break; // Don't validate all the characters in case the packet is very
598                // large
599     }
600     return true;
601   } break;
602   }
603   return false;
604 }
605 
CopyResponseValidator(const StringExtractorGDBRemote & rhs)606 void StringExtractorGDBRemote::CopyResponseValidator(
607     const StringExtractorGDBRemote &rhs) {
608   m_validator = rhs.m_validator;
609   m_validator_baton = rhs.m_validator_baton;
610 }
611 
SetResponseValidator(ResponseValidatorCallback callback,void * baton)612 void StringExtractorGDBRemote::SetResponseValidator(
613     ResponseValidatorCallback callback, void *baton) {
614   m_validator = callback;
615   m_validator_baton = baton;
616 }
617 
SetResponseValidatorToOKErrorNotSupported()618 void StringExtractorGDBRemote::SetResponseValidatorToOKErrorNotSupported() {
619   m_validator = OKErrorNotSupportedResponseValidator;
620   m_validator_baton = nullptr;
621 }
622 
SetResponseValidatorToASCIIHexBytes()623 void StringExtractorGDBRemote::SetResponseValidatorToASCIIHexBytes() {
624   m_validator = ASCIIHexBytesResponseValidator;
625   m_validator_baton = nullptr;
626 }
627 
SetResponseValidatorToJSON()628 void StringExtractorGDBRemote::SetResponseValidatorToJSON() {
629   m_validator = JSONResponseValidator;
630   m_validator_baton = nullptr;
631 }
632 
ValidateResponse() const633 bool StringExtractorGDBRemote::ValidateResponse() const {
634   // If we have a validator callback, try to validate the callback
635   if (m_validator)
636     return m_validator(m_validator_baton, *this);
637   else
638     return true; // No validator, so response is valid
639 }
640 
641 std::optional<std::pair<lldb::pid_t, lldb::tid_t>>
GetPidTid(lldb::pid_t default_pid)642 StringExtractorGDBRemote::GetPidTid(lldb::pid_t default_pid) {
643   llvm::StringRef view = llvm::StringRef(m_packet).substr(m_index);
644   size_t initial_length = view.size();
645   lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
646   lldb::tid_t tid;
647 
648   if (view.consume_front("p")) {
649     // process identifier
650     if (view.consume_front("-1")) {
651       // -1 is a special case
652       pid = AllProcesses;
653     } else if (view.consumeInteger(16, pid) || pid == 0) {
654       // not a valid hex integer OR unsupported pid 0
655       m_index = UINT64_MAX;
656       return std::nullopt;
657     }
658 
659     // "." must follow if we expect TID too; otherwise, we assume -1
660     if (!view.consume_front(".")) {
661       // update m_index
662       m_index += initial_length - view.size();
663 
664       return {{pid, AllThreads}};
665     }
666   }
667 
668   // thread identifier
669   if (view.consume_front("-1")) {
670     // -1 is a special case
671     tid = AllThreads;
672   } else if (view.consumeInteger(16, tid) || tid == 0 || pid == AllProcesses) {
673     // not a valid hex integer OR tid 0 OR pid -1 + a specific tid
674     m_index = UINT64_MAX;
675     return std::nullopt;
676   }
677 
678   // update m_index
679   m_index += initial_length - view.size();
680 
681   return {{pid != LLDB_INVALID_PROCESS_ID ? pid : default_pid, tid}};
682 }
683