]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/hal/common/v2_0/src/thread-packets.c
012360d506960d29fac9a9ab9e6abefb2a16ee82
[karo-tx-redboot.git] / packages / hal / common / v2_0 / src / thread-packets.c
1 //========================================================================
2 //
3 //      thread-packets.c
4 //
5 //      Provides multi-threaded debug support
6 //
7 //========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 //
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
16 //
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 // for more details.
21 //
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 //
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
32 //
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
35 //
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //========================================================================
41 //#####DESCRIPTIONBEGIN####
42 //
43 // Author(s):     Red Hat, nickg
44 // Contributors:  Red Hat, nickg
45 // Date:          1998-08-25
46 // Purpose:       
47 // Description:   Provides multi-threaded debug support
48 // Usage:         
49 //
50 //####DESCRIPTIONEND####
51 //
52 //========================================================================
53
54 // Define __ECOS__; allows all eCos specific additions to be easily identified.
55 #define __ECOS__
56
57
58 // #ifdef __ECOS__
59 #include <pkgconf/hal.h>
60
61 #if defined(CYGDBG_HAL_DEBUG_GDB_THREAD_SUPPORT) \
62     && defined(CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS)
63 // #endif // __ECOS__
64
65 /* FIXME: Scan this module for correct sizes of fields in packets */
66
67 #ifdef __ECOS__
68 #include <cyg/hal/dbg-threads-api.h>
69 #else  // __ECOS__
70 #include "dbg-threads-api.h"
71 #endif // __ECOS__
72
73 /* This file should ALWAYS define debug thread support */
74 /* Dont include the object in the link if you dont need the support */
75 /* This is NOT the internal unit debug flag, it is a feature control */
76 #if defined(DEBUG_THREADS)
77 #undef DEBUG_THREADS
78 #endif
79
80 #define DEBUG_THREADS 1
81 #define UNIT_TEST     0
82 #define GDB_MOCKUP    0
83
84
85 #define STUB_BUF_MAX 300 /* for range checking of packet lengths */
86      
87 #include "thread-pkts.h"
88
89 #ifdef __ECOS__
90 // Use HAL rather than board.h in eCos
91 #include <cyg/hal/hal_stub.h>
92 #else  // __ECOS__
93 #include "board.h"
94 #endif // __ECOS__
95
96 /*
97  * Export the continue and "general" (context) thread IDs from GDB.
98  */
99 int _gdb_cont_thread ;
100 int _gdb_general_thread ;
101
102 #if !defined(PKT_DEBUG)
103 #define PKT_DEBUG 0
104 #endif
105 extern void output_string(char * message) ;
106
107 #if PKT_DEBUG
108 void output_threadid(char * title,threadref * ref) ;
109 #warning "PKT_DEBUG macros engaged"
110 #define PKT_TRACE(title,packet) \
111 { output_string(title) ; output_string(packet) ; output_string("\n") ;}
112 #else
113 #define PKT_TRACE(title,packet) {}
114 #endif 
115
116
117 /* This is going to be irregular because the various implementations
118    have adopted different names for registers.
119    It would be nice to fix them to have a common convention
120      _stub_registers
121      stub_registers
122      alt_stub_registers
123  */
124
125 extern target_register_t * _registers ;
126     /* A pointer to the current set of registers */
127 extern target_register_t registers[]; /* The current saved registers */
128 extern target_register_t alt_registers[] ;
129     /* Thread or saved process state */
130
131
132 static void stub_copy_registers(
133                            target_register_t * dest,
134                            target_register_t *src
135                            )
136 {
137   target_register_t * limit ;
138   limit = dest + NUMREGS ;
139
140   while (dest < limit)  *dest++ = *src++ ;
141 }
142
143 #ifdef __ECOS__
144 void __stub_copy_registers(target_register_t * dest,
145                            target_register_t *src)
146 {
147     stub_copy_registers(dest, src);
148 }
149 #endif // __ECOS__
150
151 extern int stubhex(char ch) ;
152
153 /* ----- STUB_PACK_NAK ----------------------------------- */
154 /* Pack an error response into the response packet */
155
156 char * stub_pack_nak(char * outbuf)
157 {
158   *outbuf++ = 'E' ;
159   *outbuf++ = '0' ;
160   *outbuf++ = '2' ;
161   return outbuf ;
162 } /* stub_pack_nak */
163
164 /* ----- STUB_PACK_ACK -------------------------- */
165 /* Pack an OK achnowledgement */
166 char * stub_pack_ack(char * outbuf)
167 {
168   *outbuf++ = 'O' ;
169   *outbuf++ = 'K' ;
170   return outbuf ;
171 } /* stub_pack_ack */
172
173 /* ------- STUB_UNPACK_INT ------------------------------- */
174 /* Unpack a few bytes and return its integer value         */
175 /* This is where I wish functions could return several values
176    I would also advance the buffer pointer */
177
178 int stub_unpack_int(char * buff,int fieldlength)
179 {
180   int retval = 0 ;
181   int nibble ;
182   while (fieldlength)
183     { nibble = stubhex(*buff++) ;
184       retval |= nibble ;
185       fieldlength-- ;
186       if (fieldlength) retval = retval << 4 ;
187     }
188   return retval ;
189 } /* stub_unpack_int */
190
191 static char * unpack_byte(char * buf, int * value)
192 {
193   *value = stub_unpack_int(buf,2) ;
194   return buf + 2 ;
195 }
196
197 static char * unpack_int(char * buf, int * value)
198 {
199   *value = stub_unpack_int(buf,8) ;
200   return buf + 8 ;
201 }
202
203 /* We are NOT depending upon extensive libraries */
204 static int ishex(char ch,int *val)
205 {
206   if ((ch >= 'a') && (ch <= 'f'))
207     { *val =ch - 'a' + 10 ; return 1 ; }
208   if ((ch >= 'A') && (ch <= 'F'))
209     { *val = ch - 'A' + 10 ; return 1 ;}
210   if ((ch >= '0') && (ch <= '9'))
211     { *val = ch - '0' ; return 1 ; }
212   return 0 ;
213 } /* ishex */
214
215 static char * unpack_nibble(char * buf,int * val) 
216 {
217   ishex(*buf++,val) ;
218   return buf ;
219 }
220
221
222 static const char hexchars[] = "0123456789abcdef";
223
224 static char * pack_hex_byte(char * pkt, unsigned char byte)
225 {
226   *pkt++ = hexchars[(byte >> 4) & 0xf] ;
227   *pkt++ = hexchars[(byte & 0xf)] ;
228   return pkt ;
229 } /* pack_hex_byte */
230
231 #ifndef __ECOS__
232 /* ---- STUB_PACK_VARLEN_HEX ------------------------------------- */
233 /* Format a variable length stream of hex bytes */
234
235 static char * pack_varlen_hex(
236                        char * pkt,
237                        unsigned int value)
238 {
239   int i ;
240   static unsigned char n[8] ;
241   if (value == 0)
242     {
243       *pkt++ = '0' ;
244       return pkt ;
245     }
246   else
247     {
248       i = 8 ;
249       while (i-- >= 0 )  /* unpack nibbles into a char array */
250         {
251           n[i] = value & 0x0f ;
252           value = value >> 4 ;
253         }
254       i = 0 ;                  /* we had decrmented it to -1 */
255       while (n[i] == 0 ) i++ ; /* drop leading zeroes */
256       while (i++ < 8) *pkt++ = hexchars[n[i]] ; /* pack the number */
257     }
258   return pkt ;
259 } /* pack_varlen_hex */
260 #endif // !__ECOS__
261
262
263 /* ------ STUB_UNPACK_VARLEN_HEX --------------------------------  */
264 /* Parse a stream of hex bytes which may be of variable length     */
265 /* return the pointer to the next char                             */
266 /* modify a varparm containing the result                          */
267 /* A failure would look like a non-increment of the buffer pointer */
268
269 /* This unpacks hex strings that may have been packed using sprintf(%x) */
270 /* We assume some non-hex delimits them */
271
272 char * unpack_varlen_hex(
273                               char * buff,    /* packet to parse */
274                               int * result)
275 {
276   int nibble ;
277   int retval ;
278   retval = 0 ;
279
280   while (ishex(*buff,&nibble))
281     {
282       buff++ ;
283       retval = retval  << 4 ;
284       retval |= nibble & 0x0f ;
285     }
286   *result = retval ;
287   return buff ;
288 } /* stub_unpack_varlen_int */
289
290
291 /* ------ UNPACK_THREADID ------------------------------- */
292 /* A threadid is a 64 bit quantity                        */
293
294 #define BUFTHREADIDSIZ 16 /* encode 64 bits in 16 chars of hex */
295
296 static char * unpack_threadid(char * inbuf, threadref * id)
297 {
298   char * altref ;
299   char * limit  = inbuf + BUFTHREADIDSIZ ;
300   int x,y ;
301   altref = (char *) id ;
302
303   while (inbuf < limit)
304     {
305       x = stubhex(*inbuf++) ;
306       y = stubhex(*inbuf++) ;
307       *altref++ = (x << 4) | y ;
308     }
309   return inbuf ;
310 } /* unpack_threadid */
311
312
313
314 /* Pack an integer use leading zeroes */
315 static char * pack_int(char * buf,int value)
316 {
317   buf = pack_hex_byte(buf,(value>> 24)& 0xff) ;
318   buf = pack_hex_byte(buf,(value >>16)& 0xff) ;
319   buf = pack_hex_byte(buf,(value >>8) & 0x0ff) ;
320   buf = pack_hex_byte(buf,(value & 0xff)) ;
321   return buf ;
322 } /* pack_int */
323
324
325 /* -------- PACK_STRING ---------------------------------------------- */
326 /* This stupid string better not contain any funny characters */
327 /* Also, the GDB protocol will not cope with NULLs in the
328    string or at the end of it.
329    While is is posable to encapsulate the protocol in ays that
330    preclude filtering for # I am assuming this is a constraint.
331 */
332
333 static char * pack_raw_string(char * pkt,char * string)
334 {
335   char ch ;
336   while (0 != (ch = *string++)) *pkt++ = ch ;
337   return pkt ;
338 }
339
340 static char * pack_string(
341                           char * pkt,
342                           char * string)
343 {
344   char ch ;
345 #ifdef __ECOS__
346   int len = 0;
347   char *s = string;
348   while( *s++ ) len++;
349 #else  // __ECOS__
350   int len ;
351   len = strlen(string) ;
352 #endif // __ECOS
353   if (len > 200 ) len = 200 ; /* Bigger than most GDB packets, junk??? */
354   pkt = pack_hex_byte(pkt,len) ;
355   while (len-- > 0)
356     { 
357        ch = *string++ ;
358        if ((ch == '\0') || (ch == '#')) ch = '*' ; /* Protect encapsulation */
359        *pkt++ = ch ;
360     }
361   return pkt ;
362 } /* pack_string */
363
364
365 /* ----- STUB_PACK_THREADID  --------------------------------------------- */
366 /* Convert a binary 64 bit threadid  and pack it into a xmit buffer */
367 /* Return the advanced buffer pointer */
368
369 static char * pack_threadid(char * pkt, threadref * id)
370 {
371   char * limit ;
372   unsigned char * altid ;
373   altid = (unsigned char *) id ;
374   limit = pkt + BUFTHREADIDSIZ ;
375   while (pkt < limit) pkt = pack_hex_byte(pkt,*altid++) ;
376   return pkt ;
377 } /* stub_pack_threadid */
378
379 /* UNFORTUNATELY, not all of the extended debugging system has yet been
380    converted to 64 but thread references and process identifiers.
381    These routines do the conversion.
382    An array of bytes is the correct treatment of an opaque identifier.
383    ints have endian issues.
384  */
385
386 static void int_to_threadref(threadref * id, int value)
387 {
388   unsigned char * scan ;
389   scan = (unsigned char *) id ;
390    {
391     int i = 4 ;
392     while (i--) *scan++  = 0 ;
393   }
394   *scan++ = (value >> 24) & 0xff ;
395   *scan++ = (value >> 16) & 0xff ;
396   *scan++ = (value >> 8) & 0xff ;
397   *scan++ = (value & 0xff) ;
398 }
399
400 static int threadref_to_int(threadref * ref)
401 {
402   int value = 0 ;
403   unsigned char * scan ;
404   int i ;
405   
406   scan = (char *) ref ;
407   scan += 4 ;
408   i = 4 ;
409   while (i-- > 0) value = (value << 8) | ((*scan++) & 0xff) ;
410   return value ;
411 } /* threadref_to_int */
412
413 void copy_threadref(threadref * dest, threadref * src)
414 {
415   int i ;
416   unsigned char * csrc, * cdest ;
417   csrc = (unsigned char *) src ;
418   cdest = (unsigned char *) dest ;
419   i = 8 ;
420   while (i--) *cdest++ = *csrc++ ;
421 }
422
423
424 int threadmatch(
425                 threadref * dest ,
426                 threadref * src
427                 )
428 {
429   unsigned char * srcp, * destp ;
430   int i , result ;
431   srcp = (char *) src ;
432   destp = (char *) dest ;
433   i = 8 ;
434   result = 1 ;
435   while (i-- > 0 ) result &= (*srcp++ == *destp++) ? 1 : 0 ;
436   return result ;
437 } /* threadmatch */           
438
439
440
441 static char * Tpkt_threadtag = "thread:" ;
442
443 /* ----- STUB_PACK_TPKT_THREADID ------------------------------------ */
444 /* retreive, tag and insert a thread identifier into a T packet. */
445 /* Insert nothing if the thread identifier is not available */
446
447 char * stub_pack_Tpkt_threadid(char * pkt)
448 {
449   static threadref thread ;
450   int fmt = 0 ; /* old format */
451   PKT_TRACE("Tpkt-id","---") ;
452   if (dbg_currthread(&thread))
453     {
454       pkt = pack_raw_string(pkt,Tpkt_threadtag) ;
455       if (fmt)
456         pkt = pack_threadid(pkt,&thread) ;
457       else
458           /* Until GDB lengthens its thread ids, we have to MASH
459              the threadid into somthing shorter. PLEASE FIX GDB */
460           pkt = pack_int(pkt,threadref_to_int(&thread)) ;
461       *pkt++ = ';'  ; /* terminate variable length int */
462       *pkt = '\0' ; /* Null terminate to allow string to be printed, no++ */
463     }
464   PKT_TRACE("packedTpkt","--") ;
465   return pkt ;
466 } /* stub_pack_Tpkt_threadid */
467
468 long stub_get_currthread (void)
469 {
470   threadref thread ;
471   
472   if (dbg_currthread(&thread))
473     return threadref_to_int(&thread) ;
474   else
475     return 0 ;
476 }
477
478 void stub_pkt_currthread(
479                                 char * inbuf,
480                                 char * outbuf,
481                                 int bufmax)
482 {
483   threadref thread ;
484   char * base_out ;
485   base_out = outbuf ;
486   
487   if (dbg_currthread(&thread))
488     {
489       *outbuf++ = 'Q' ;
490       *outbuf++ = 'C' ; /* FIXME: Is this a reasonable code */
491       outbuf = pack_int(outbuf, threadref_to_int(&thread)) ; /* Short form */
492     }
493   else outbuf = stub_pack_nak(outbuf) ;
494   *outbuf = '\0' ; /* terminate response packet */
495   PKT_TRACE("stub_pkt_currthread(resp) ",base_out) ;
496 } /* stub_pkt_currthread */
497
498 /* ----- STUB_PKT_THREAD_ALIVE --------------------------------- */
499 /* Answer the thread alive query */
500
501 static int thread_alive (int id)
502 {
503   threadref thread ;
504   struct cygmon_thread_debug_info info ;
505
506   int_to_threadref(&thread, id) ;
507   if (dbg_threadinfo(&thread, &info) &&
508       info.context_exists)
509     return 1 ;
510   else
511     return 0 ;
512 }
513
514 void stub_pkt_thread_alive(char * inbuf,
515                            char * outbuf,
516                            int bufmax)
517 {
518   char * prebuf = inbuf ;
519   int result ;
520   
521   if (prebuf != (inbuf = unpack_varlen_hex(inbuf,&result)))
522     {
523       if (thread_alive(result))
524         {
525           outbuf = stub_pack_ack(outbuf) ;
526           *outbuf = '\0' ;
527           return ;
528         }
529     }
530   outbuf = stub_pack_nak(outbuf) ;
531   *outbuf = '\0' ; /* terminate the response message */
532 } /* stub_pkt_thread_alive */
533
534  
535
536 /* ----- STUB_PKT_CHANGETHREAD ------------------------------- */
537 /* Switch the display of registers to that of a saved context */
538
539 /* Changing the context makes NO sense, although the packets define the
540    capability. Therefore, the option to change the context back does
541    call the function to change registers. Also, there is no
542    forced context switch.
543      'p' - New format, long long threadid, no special cases
544      'c' - Old format, id for continue, 32 bit threadid max, possably less
545                        -1 means continue all threads
546      'g' - Old Format, id for general use (other than continue)
547
548      replies:
549           OK for success
550           ENN for error
551    */
552
553 void stub_pkt_changethread(
554                            char * inbuf,
555                            char * outbuf,
556                            int bufmax)
557 {
558   threadref id ;
559   int idefined = -1 ;
560   char ch ;
561   PKT_TRACE("setthread-pkt ",inbuf) ;
562
563   /* Parse the incoming packet for a thread identifier */
564   switch (ch = *inbuf++ )  /* handle various packet formats */
565     {
566     case 'p' : /* New format: mode:8,threadid:64 */
567       inbuf = unpack_nibble(inbuf,&idefined) ;
568       inbuf = unpack_threadid(inbuf,&id) ; /* even if startflag */
569       break ;  
570     case 'c' : /* old format , specify thread for continue */
571       if (inbuf[0] == '-' && inbuf[1] == '1')   /* Hc-1 */
572         _gdb_cont_thread = 0 ;
573       else
574         inbuf = unpack_varlen_hex(inbuf, &_gdb_cont_thread) ;
575
576       if (_gdb_cont_thread == 0 ||              /* revert to any old thread */
577           thread_alive(_gdb_cont_thread))       /* specified thread is alive */
578         outbuf = stub_pack_ack(outbuf) ;
579       else
580         outbuf = stub_pack_nak(outbuf) ;
581       break ;
582     case 'g' : /* old format, specify thread for general operations */
583       /* OLD format: parse a variable length hex string */
584       /* OLD format consider special thread ids */
585       {
586         inbuf = unpack_varlen_hex(inbuf, &_gdb_general_thread) ;
587         int_to_threadref(&id, _gdb_general_thread) ;
588         switch (_gdb_general_thread)
589           {
590           case  0 : /* pick a thread, any thread */
591             idefined = 2 ; /* select original interrupted context */
592             break ;
593           case -1 : /* all threads */
594             idefined = 2 ;
595             break ;
596           default :
597             idefined = 1 ; /* select the specified thread */
598             break ;
599           }
600       }
601       break ;
602     default:
603       outbuf = stub_pack_nak(outbuf) ;
604       break ;
605     } /* handle various packet formats */
606
607   switch (idefined)
608     {
609     case -1 :
610       /* Packet not supported, already NAKed, no further action */
611       break ;
612     case 0 :
613       /* Switch back to interrupted context */
614       _registers = &registers[0] ;
615       break ;
616     case 1 :
617       /* copy the saved registers into the backup registers */
618       stub_copy_registers(alt_registers,registers) ;
619       /* The OS will now update the values it has in a saved process context*/
620       if (dbg_getthreadreg(&id,NUMREGS,&alt_registers[0]))
621         {
622           /* switch the registers pointer */
623           _registers = &alt_registers[0] ;
624           outbuf = stub_pack_ack(outbuf) ; 
625         }
626       else
627           outbuf = stub_pack_nak(outbuf) ;
628       break ;
629     case 2 :
630       /* switch to interrupted context */ 
631       outbuf = stub_pack_ack(outbuf) ;
632       break ;
633     default:
634       outbuf = stub_pack_nak(outbuf) ;
635       break ;
636     }
637   *outbuf = '\0' ; /* Terminate response pkt */
638 } /* stub_pkt_changethread */
639
640
641 /* ---- STUB_PKT_GETTHREADLIST ------------------------------- */
642 /* Get a portion of the threadlist  or process list            */
643 /* This may be part of a multipacket transaction               */
644 /* It would be hard to tell in the response packet the difference
645    between the end of list and an error in threadid encoding.
646    */
647
648 void stub_pkt_getthreadlist(char * inbuf,
649                             char * outbuf,
650                             int bufmax)
651 {
652   char * count_ptr ;
653   char * done_ptr ;
654   char * limit ;
655   int start_flag , batchsize , result , count ;
656   static threadref lastthread, nextthread ;
657
658 #if PKT_DEBUG
659   char * r_base = outbuf ;
660 #endif  
661   PKT_TRACE("pkt_getthreadlist: ",inbuf) ;
662
663   count = 0 ;
664   inbuf = unpack_nibble(inbuf,&start_flag) ;
665   inbuf = unpack_byte(inbuf,&batchsize) ;
666   inbuf = unpack_threadid(inbuf,&lastthread) ; /* even if startflag */
667
668   /*   Start building response packet    */
669   limit = outbuf + (bufmax - BUFTHREADIDSIZ - 10) ; /* id output packing limit */
670   *outbuf++ = 'Q'  ;
671   *outbuf++ = 'M'  ;
672   
673   /* Default values for count and done fields, save ptr to repatch */
674   count_ptr = outbuf ; /* save to repatch count */
675   outbuf = pack_hex_byte(outbuf,0) ;
676   done_ptr = outbuf ;  /* Backpatched later */
677   *outbuf++ = '0' ;    /* Done = 0 by default */
678   outbuf = pack_threadid(outbuf,&lastthread) ;
679   
680   /* Loop through the threadid packing */
681   while ((outbuf < limit) && (count < batchsize))
682     {
683       result = dbg_threadlist(start_flag,&lastthread,&nextthread) ;
684       start_flag = 0 ; /* redundant but effective */
685       if (!result)
686         { *done_ptr = '1' ;   /* pack the done flag */
687           break ;
688         }
689 #if 0 /* DEBUG */
690       if (threadmatch(&lastthread,&nextthread))
691         {
692           output_string("FAIL: Threadlist, not incrementing\n") ;
693           *done_ptr = '1' ;
694           break ;
695         }
696 #endif      
697       count++ ;
698       outbuf = pack_threadid(outbuf,&nextthread) ;
699       copy_threadref(&lastthread,&nextthread) ;
700     }
701   pack_hex_byte(count_ptr,count) ;/* backpatch, Pack the count field */
702   *outbuf = '\0' ;
703   PKT_TRACE("pkt_getthreadlist(resp) ",r_base) ;
704 } /* pkt_getthreadlist */
705
706
707
708
709 /* ----- STUB_PKT_GETTHREADINFO ---------------------------------------- */
710 /* Get the detailed information about a specific thread or process */
711
712 /*
713 Encoding:
714  'Q':8,'P':8,mask:16
715
716  Mask Fields
717         threadid:1,        # always request threadid 
718         context_exists:2,
719         display:4,          
720         unique_name:8,
721         more_display:16
722  */  
723
724 void stub_pkt_getthreadinfo(
725                             char * inbuf,
726                             char * outbuf,
727                             int bufmax)
728 {
729   int mask ;
730   int result ;
731   threadref thread ;
732   struct cygmon_thread_debug_info info ;
733
734   info.context_exists = 0 ;
735   info.thread_display = 0 ;
736   info.unique_thread_name = 0 ;
737   info.more_display = 0 ;
738
739   /* Assume the packet identification chars have already been
740      discarded by the packet demultiples routines */
741   PKT_TRACE("PKT getthreadinfo",inbuf) ;
742   
743   inbuf = unpack_int(inbuf,&mask) ;
744   inbuf = unpack_threadid(inbuf,&thread) ;
745   
746   result = dbg_threadinfo(&thread,&info) ; /* Make system call */
747
748   if (result)
749     {
750       *outbuf++ = 'q' ;
751       *outbuf++ = 'p' ;
752       outbuf = pack_int(outbuf,mask) ;
753       outbuf = pack_threadid(outbuf,&info.thread_id) ; /* echo threadid */
754       if (mask & 2)   /* context-exists */
755         {
756           outbuf = pack_int(outbuf,2) ; /* tag */
757           outbuf = pack_hex_byte(outbuf,2) ; /* length */
758           outbuf = pack_hex_byte(outbuf,info.context_exists) ;
759         }
760       if ((mask & 4)  && info.thread_display)/* display */
761         {
762           outbuf = pack_int(outbuf,4) ; /* tag */
763           outbuf = pack_string(outbuf,info.thread_display) ;
764         }
765       if ((mask & 8) && info.unique_thread_name) /* unique_name */
766         {
767           outbuf = pack_int(outbuf,8) ;
768           outbuf = pack_string(outbuf,info.unique_thread_name) ;
769         }
770       if ((mask & 16) && info.more_display)  /* more display */
771         {
772           outbuf = pack_int(outbuf,16) ; /* tag 16 */
773           outbuf = pack_string(outbuf,info.more_display) ;
774         }
775     }
776   else
777     {
778       PKT_TRACE("FAIL: dbg_threadinfo\n", "") ;
779       outbuf = stub_pack_nak(outbuf) ;
780     }
781   *outbuf = '\0' ;
782 } /* stub_pkt_getthreadinfo */
783
784 int stub_lock_scheduler(int lock,       /* 0 to unlock, 1 to lock */
785                         int mode,       /* 0 for step,  1 for continue */
786                         long id)        /* current thread */
787 {
788   threadref thread;
789
790   int_to_threadref(&thread, id) ;
791   return dbg_scheduler(&thread, lock, mode) ;
792 }
793
794 \f
795 #if GDB_MOCKUP
796 /* ------ MATCHED GDB SIDE PACKET ENCODING AND PARSING ----------------- */
797
798 char * pack_nibble(char * buf, int nibble)
799 {
800   *buf++ =  hexchars[(nibble & 0x0f)] ;
801   return buf ;
802 } /* pack_nibble */
803
804 #if 0
805 static char * unpack_short(char * buf,int * value)
806 {
807   *value = stub_unpack_int(buf,4) ;
808   return buf + 4 ;
809 }
810
811 static char * pack_short(
812                   char * buf,
813                   unsigned int value)
814 {
815   buf = pack_hex_byte(buf,(value >> 8) & 0xff) ;
816   buf = pack_hex_byte(buf,(value & 0xff)) ;
817   return buf ;
818 } /* pack_short */
819 #endif
820
821
822 /* Generally, I dont bury xmit and receive calls inside packet formatters
823    and parsers
824    */
825
826
827
828
829 /* ----- PACK_SETTHREAD_REQUEST ------------------------------------- */
830 /*      Encoding: ??? decode gdb/remote.c
831         'Q':8,'p':8,idefined:8,threadid:32 ;
832         */
833
834 char * pack_setthread_request(
835                        char * buf,
836                        char fmt,   /* c,g or, p */
837                        int idformat ,
838                        threadref *  threadid )
839 {
840   *buf++ = fmt ;
841   
842   if (fmt == 'p')
843     {  /* pack the long form */
844       buf = pack_nibble(buf,idformat) ;
845       buf = pack_threadid(buf,threadid)  ;
846     }
847   else
848     {  /* pack the shorter form - Serious truncation */
849       /* There are reserved identifieds 0 , -1 */
850       int quickref = threadref_to_int(threadid) ;
851       buf = pack_varlen_hex(buf,quickref) ;
852     }
853   *buf++ = '\0' ; /* end_of_packet  */
854   return buf ;
855 } /* pack_setthread_request */
856
857
858
859 /* -------- PACK_THREADLIST-REQUEST --------------------------------- */
860 /*    Format: i'Q':8,i"L":8,initflag:8,batchsize:16,lastthreadid:32   */
861
862
863 char * pack_threadlist_request(
864                                char * pkt,
865                                int startflag,
866                                int threadcount,
867                                threadref * nextthread 
868                                )
869 {
870   *pkt++ = 'q' ;
871   *pkt++ = 'L' ;
872   pkt = pack_nibble(pkt,startflag) ;     /* initflag 1 bytes */
873   pkt = pack_hex_byte(pkt,threadcount) ;   /* threadcount 2 bytes */
874   pkt = pack_threadid(pkt,nextthread) ;        /* 64 bit thread identifier */
875   *pkt = '\0' ;
876   return pkt ;
877 } /* remote_threadlist_request */
878
879
880
881
882 /* ---------- PARSE_THREADLIST_RESPONSE ------------------------------------ */
883 /* Encoding:   'q':8,'M':8,count:16,done:8,argthreadid:64,(threadid:64)* */
884
885 int parse_threadlist_response(
886                               char * pkt,
887                               threadref * original_echo,
888                               threadref * resultlist,
889                               int * doneflag)
890 {
891   char * limit ;
892   int count, resultcount , done ;
893   resultcount = 0 ;
894
895   /* assume the 'q' and 'M chars have been stripped */
896   PKT_TRACE("parse-threadlist-response ",pkt) ;
897   limit = pkt + (STUB_BUF_MAX - BUFTHREADIDSIZ) ; /* done parse past here */
898   pkt = unpack_byte(pkt,&count)  ;                /* count field */
899   pkt = unpack_nibble(pkt,&done) ;
900   /* The first threadid is the argument threadid */
901   pkt = unpack_threadid(pkt,original_echo) ; /* should match query packet */
902   while ((count-- > 0) && (pkt < limit))
903     {
904       pkt = unpack_threadid(pkt,resultlist++) ;
905       resultcount++ ;
906     }
907   if (doneflag) *doneflag = done ;
908   return resultcount ; /* successvalue */
909 } /* parse_threadlist_response */
910  
911 struct gdb_ext_thread_info
912 {
913   threadref threadid ;
914   int active ;
915   char display[256] ;
916   char shortname[32] ;
917   char more_display[256] ;
918 } ;
919
920
921 /* ----- PACK_THREAD_INFO_REQUEST -------------------------------- */
922
923 /* 
924    threadid:1,        # always request threadid
925    context_exists:2,
926    display:4,
927    unique_name:8,
928    more_display:16
929  */
930
931 /* Encoding:  'Q':8,'P':8,mask:32,threadid:64 */
932
933 char * pack_threadinfo_request(char * pkt,
934                                 int mode,
935                                 threadref * id 
936                                 )
937 {
938   *pkt++ = 'Q' ;
939   *pkt++ = 'P' ;
940   pkt = pack_int(pkt,mode) ; /* mode */
941   pkt = pack_threadid(pkt,id) ; /* threadid */
942   *pkt = '\0' ; /* terminate */
943   return pkt ;
944 } /* pack_thread_info_request */
945
946
947
948 static char * unpack_string(
949                             char * src,
950                             char * dest,
951                             int length)
952 {
953   while (length--) *dest++ = *src++ ;
954   *dest = '\0' ;
955   return src ;
956 } /* unpack_string */
957
958
959 void output_threadid(char * title,threadref * ref)
960 {
961   char hexid[20] ;
962   pack_threadid(&hexid[0],ref) ; /* Convert threead id into hex */
963   hexid[16] = 0 ;
964   output_string(title) ; 
965   output_string(&hexid[0]) ; 
966   output_string("\n") ;
967 }
968
969 /* ------ REMOTE_UPK_THREAD_INFO_RESPONSE ------------------------------- */
970 /* Unpack the response of a detailed thread info packet */
971 /* Encoding:  i'Q':8,i'R':8,argmask:16,threadid:64,(tag:8,length:16,data:x)* */
972
973 #define TAG_THREADID 1
974 #define TAG_EXISTS 2
975 #define TAG_DISPLAY 4
976 #define TAG_THREADNAME 8
977 #define TAG_MOREDISPLAY 16 
978
979
980 int remote_upk_thread_info_response(
981                                char * pkt,
982                                threadref * expectedref ,
983                                struct gdb_ext_thread_info * info)
984 {
985   int mask, length ;
986   unsigned int tag ;
987   threadref ref ;
988   char * limit = pkt + 500 ; /* plausable parsing limit */
989   int retval = 1 ;
990
991   PKT_TRACE("upk-threadinfo ",pkt) ;
992
993   /* info->threadid = 0 ; FIXME: implement zero_threadref */
994   info->active = 0 ;
995   info->display[0] = '\0' ;
996   info->shortname[0] = '\0' ;
997   info->more_display[0] = '\0' ;
998
999   /* Assume the characters indicating the packet type have been stripped */
1000   pkt = unpack_int(pkt,&mask) ;  /* arg mask */
1001   pkt = unpack_threadid(pkt , &ref) ;
1002                       
1003   if (! threadmatch(&ref,expectedref))
1004     { /* This is an answer to a different request */
1005       output_string("FAIL Thread mismatch\n") ;
1006       output_threadid("ref ",&ref) ;
1007       output_threadid("expected ",expectedref) ;
1008       return 0 ;
1009     }
1010   copy_threadref(&info->threadid,&ref) ;
1011   
1012   /* Loop on tagged fields , try to bail if somthing goes wrong */
1013   if (mask==0)  output_string("OOPS NO MASK \n") ;
1014
1015   while ((pkt < limit) && mask && *pkt)  /* packets are terminated with nulls */
1016     {
1017       pkt = unpack_int(pkt,&tag) ;            /* tag */
1018       pkt = unpack_byte(pkt,&length) ;   /* length */
1019       if (! (tag & mask))  /* tags out of synch with mask */
1020         {
1021           output_string("FAIL: threadinfo tag mismatch\n") ;
1022           retval = 0 ;
1023           break ;
1024         }
1025       if (tag == TAG_THREADID)
1026         {
1027           output_string("unpack THREADID\n") ;
1028           if (length != 16)
1029             {
1030               output_string("FAIL: length of threadid is not 16\n") ;
1031               retval = 0 ;
1032               break ;
1033             }
1034           pkt = unpack_threadid(pkt,&ref) ;
1035           mask = mask & ~ TAG_THREADID ;
1036           continue ;
1037         }
1038       if (tag == TAG_EXISTS)
1039         {
1040           info->active = stub_unpack_int(pkt,length) ;
1041           pkt += length ;
1042           mask = mask & ~(TAG_EXISTS) ;
1043           if (length > 8)
1044             {
1045               output_string("FAIL: 'exists' length too long\n") ;
1046               retval = 0 ;
1047               break ;
1048             }
1049           continue ;
1050         }
1051       if (tag == TAG_THREADNAME)
1052         {
1053           pkt = unpack_string(pkt,&info->shortname[0],length) ;
1054           mask = mask & ~TAG_THREADNAME ;
1055           continue ;
1056         }
1057       if (tag == TAG_DISPLAY)
1058         { 
1059           pkt = unpack_string(pkt,&info->display[0],length) ;
1060           mask = mask & ~TAG_DISPLAY ;
1061           continue ;
1062         }
1063       if (tag == TAG_MOREDISPLAY)
1064         { 
1065           pkt = unpack_string(pkt,&info->more_display[0],length) ;
1066           mask = mask & ~TAG_MOREDISPLAY ;
1067           continue ;
1068         }
1069       output_string("FAIL: unknown info tag\n") ;
1070       break ; /* Not a tag we know about */
1071     }
1072   return retval  ;
1073 } /* parse-thread_info_response */
1074
1075
1076 /* ---- REMOTE_PACK_CURRTHREAD_REQUEST ---------------------------- */
1077 /* This is a request to emit the T packet */
1078
1079 /* FORMAT: 'q':8,'C' */
1080
1081 char * remote_pack_currthread_request(char * pkt )
1082 {
1083   *pkt++ = 'q' ;
1084   *pkt++ = 'C' ;
1085   *pkt = '\0' ;
1086   return pkt ;
1087 } /* remote_pack_currthread_request */
1088
1089
1090 /* ------- REMOTE_UPK_CURTHREAD_RESPONSE ----------------------- */
1091 /* Unpack the interesting part of a T packet */
1092
1093
1094 int remote_upk_currthread_response(
1095                                char * pkt,
1096                                int *thr )  /* Parse a T packet */
1097 {
1098   int retval = 0 ;
1099   PKT_TRACE("upk-currthreadresp ",pkt) ;
1100
1101 #if 0
1102   {
1103     static char threadtag[8] =  "thread" ;
1104     int retval = 0 ;
1105     int i , found ;
1106     char ch ;
1107     int quickid ;
1108
1109   /* Unpack as a t packet */
1110   while (((ch = *pkt++) != ':')    /* scan for : thread */
1111          && (ch != '\0'))          /* stop at end of packet */
1112
1113     {
1114       found = 0 ;
1115       i = 0 ;
1116       while ((ch = *pkt++) == threadtag[i++]) ;
1117       if (i == 8) /* string match "thread" */
1118         {
1119           pkt = unpack_varlen_hex(pkt,&quickid) ;
1120           retval = 1;
1121           break ;
1122         }
1123       retval = 0 ;
1124     }
1125   }
1126 #else
1127   pkt = unpack_threadid(pkt, thr) ;
1128   retval = 1 ;
1129 #endif  
1130   return retval ;
1131 } /* remote_upk_currthread_response */
1132
1133
1134 /* -------- REMOTE_UPK-SIMPLE_ACK --------------------------------- */
1135 /* Decode a response which is eother "OK" or "Enn"
1136    fillin error code,  fillin pkfag-1== undef, 0==nak, 1 == ack ;
1137    return advanced packet pointer */
1138
1139
1140 char * remote_upk_simple_ack(
1141                              char * buf,
1142                              int * pkflag,
1143                              int * errcode)
1144 {
1145   int lclerr = 0 ;
1146   char ch = *buf++ ;
1147   int retval = -1 ;  /* Undefined ACK , a protocol error */
1148   if (ch == 'E')     /* NAK */
1149     {
1150       buf = unpack_byte(buf,&lclerr) ;
1151       retval = 0 ;   /* transaction failed, explicitly */
1152     }
1153   else
1154     if ((ch == 'O') && (*buf++ == 'K')) /* ACK */
1155       retval = 1 ; /* transaction succeeded */
1156   *pkflag = retval ;
1157   *errcode = lclerr ;
1158   return buf ;
1159 } /* remote-upk_simple_ack */
1160
1161
1162 /* -------- PACK_THREADALIVE_REQUEST ------------------------------- */
1163
1164 char * pack_threadalive_request(
1165                          char * buf,
1166                          threadref * threadid)
1167 {
1168   *buf++ = 'T' ;
1169   buf = pack_threadid(buf,threadid) ;
1170   *buf = '\0' ;
1171   return buf ;
1172 } /* pack_threadalive_request */
1173      
1174 #endif /* GDB_MOCKUP */
1175 \f
1176 /* ---------------------------------------------------------------------- */
1177 /* UNIT_TESTS SUBSECTION                                                  */
1178 /* ---------------------------------------------------------------------- */
1179
1180
1181 #if UNIT_TEST
1182 extern void output_string(char * message) ;
1183 static char test_req[400] ;
1184 static char t_response[400] ;
1185
1186
1187
1188 /* ----- DISPLAY_THREAD_INFO ---------------------------------------------- */
1189 /*  Use local cygmon string output utiities */
1190
1191 void display_thread_info(struct gdb_ext_thread_info * info)
1192 {
1193
1194   output_threadid("Threadid: ",&info->threadid) ;
1195   /* short name */
1196   output_string("Name: ") ; output_string(info->shortname) ; output_string("\n");
1197   /* format display state */
1198   output_string("State: ") ; output_string(info->display) ; output_string("\n") ;
1199   /* additional data */
1200   output_string("other: ");output_string(info->more_display);
1201    output_string("\n\n");
1202 } /* display_thread_info */
1203
1204
1205 /* --- CURRTHREAD-TEST -------------------------------------------- */
1206 static int currthread_test(threadref * thread)
1207 {
1208   int result ;
1209   int threadid ;
1210   output_string("TEST: currthread\n") ;
1211   remote_pack_currthread_request(test_req) ;
1212   stub_pkt_currthread(test_req+2,t_response,STUB_BUF_MAX) ;
1213   result = remote_upk_currthread_response(t_response+2, &threadid) ;
1214   if (result)
1215     {
1216       output_string("PASS getcurthread\n") ;
1217       /* FIXME: print the thread */
1218     }
1219   else
1220     output_string("FAIL getcurrthread\n") ;
1221   return result ;
1222 } /* currthread_test */
1223
1224 /* ------ SETTHREAD_TEST ------------------------------------------- */
1225   /* use a known thread from previous test */
1226
1227 static int setthread_test(threadref * thread)
1228 {
1229   int result, errcode ;
1230   output_string("TEST: setthread\n") ;
1231   
1232   pack_setthread_request(test_req,'p',1,thread) ;
1233   stub_pkt_changethread(test_req,t_response,STUB_BUF_MAX) ;
1234   remote_upk_simple_ack(t_response,&result,&errcode) ;
1235   switch (result)
1236     {
1237     case 0 :
1238       output_string("FAIL setthread\n") ;
1239       break ;
1240     case 1 :
1241       output_string("PASS setthread\n") ;
1242       break ;
1243     default :
1244       output_string("FAIL setthread -unrecognized response\n") ;
1245       break ;
1246     }
1247   return result ;
1248 } /* setthread_test */
1249
1250
1251 /* ------ THREADACTIVE_TEST ---------------------- */
1252   /* use known thread */
1253   /* pack threadactive packet */
1254   /* process threadactive packet */
1255   /* parse threadactive response */
1256   /* check results */
1257
1258
1259 int threadactive_test(threadref * thread)
1260 {
1261   int result ;
1262   int errcode ;
1263   output_string("TEST: threadactive\n") ;
1264   pack_threadalive_request(test_req,thread) ;
1265   stub_pkt_thread_alive(test_req+1,t_response,STUB_BUF_MAX);
1266   remote_upk_simple_ack(t_response,&result,&errcode) ;
1267   switch (result)
1268     {
1269     case 0 :
1270       output_string("FAIL threadalive\n") ;
1271       break ;
1272     case 1 :
1273       output_string("PASS threadalive\n") ;
1274       break ;
1275     default :
1276       output_string("FAIL threadalive -unrecognized response\n") ;
1277       break ;
1278     }
1279   return result ;
1280 } /* threadactive_test */
1281
1282 /* ------ REMOTE_GET_THREADINFO -------------------------------------- */
1283 int remote_get_threadinfo(
1284                            threadref * threadid,
1285                            int fieldset , /* TAG mask */
1286                            struct gdb_ext_thread_info * info
1287                           )
1288 {
1289   int result ;
1290   pack_threadinfo_request(test_req,fieldset,threadid) ;
1291   stub_pkt_getthreadinfo(test_req+2,t_response,STUB_BUF_MAX) ;
1292   result = remote_upk_thread_info_response(t_response+2,threadid,info) ;
1293   return result ;
1294 } /* remote_get-thrreadinfo */
1295
1296
1297 static struct gdb_ext_thread_info test_info ;
1298
1299 static int get_and_display_threadinfo(threadref * thread)
1300 {
1301   int mode ;
1302   int result ;
1303   /* output_string("TEST: get and display threadinfo\n") ; */
1304
1305   mode = TAG_THREADID | TAG_EXISTS | TAG_THREADNAME
1306     | TAG_MOREDISPLAY | TAG_DISPLAY ;
1307   result = remote_get_threadinfo(thread,mode,&test_info) ;
1308   if (result) display_thread_info(&test_info) ;
1309 #if 0  /* silent subtest */
1310   if (result)
1311       output_string("PASS: get_and_display threadinfo\n") ;
1312   else
1313       output_string("FAIL: get_and_display threadinfo\n") ;
1314 #endif  
1315   return result ;
1316 } /* get-and-display-threadinfo */
1317
1318
1319
1320 /* ----- THREADLIST_TEST ------------------------------------------ */
1321 #define TESTLISTSIZE 16
1322 #define TLRSIZ 2
1323 static threadref test_threadlist[TESTLISTSIZE] ;
1324
1325 static int threadlist_test(void)
1326 {
1327   int done, i , result_count ;
1328   int startflag = 1 ;
1329   int result = 1 ;
1330   int loopcount = 0 ;
1331   static threadref nextthread ;
1332   static threadref echo_nextthread ;
1333
1334   output_string("TEST: threadlist\n") ;
1335
1336   done = 0 ;
1337   while (! done)
1338     {
1339       if (loopcount++ > 10)
1340         {
1341           result = 0 ;
1342           output_string("FAIL: Threadlist test -infinite loop-\n") ;
1343           break ;
1344         }
1345       pack_threadlist_request(test_req,startflag,TLRSIZ,&nextthread) ;
1346       startflag = 0 ; /* clear for later iterations */
1347       stub_pkt_getthreadlist(test_req+2,t_response,STUB_BUF_MAX);
1348       result_count = parse_threadlist_response(t_response+2,
1349                                                &echo_nextthread,
1350                                                &test_threadlist[0],&done) ;
1351       if (! threadmatch(&echo_nextthread,&nextthread))
1352         {
1353           output_string("FAIL: threadlist did not echo arg thread\n");
1354           result = 0 ;
1355           break ;
1356         }
1357       if (result_count <= 0)
1358         {
1359           if (done != 0)
1360               { output_string("FAIL threadlist_test, failed to get list");
1361                 result = 0 ;
1362               }
1363           break ;
1364         }
1365       if (result_count > TLRSIZ)
1366         {
1367           output_string("FAIL: threadlist response longer than requested\n") ;
1368           result = 0 ;
1369           break ;
1370         }
1371       /* Setup to resume next batch of thread references , set nestthread */
1372       copy_threadref(&nextthread,&test_threadlist[result_count-1]) ;
1373       /* output_threadid("last-of-batch",&nextthread) ; */
1374       i = 0 ;
1375       while (result_count--)
1376           {
1377             if (0)  /* two display alternatives */
1378               output_threadid("truncatedisplay",&test_threadlist[i++]) ;
1379             else
1380               get_and_display_threadinfo(&test_threadlist[i++]) ; 
1381           }
1382
1383     }
1384   if (!result)
1385     output_string("FAIL: threadlist test\n") ;
1386   else output_string("PASS: Threadlist test\n") ;
1387   return result ;
1388 } /* threadlist_test */
1389
1390
1391 static threadref testthread ;
1392
1393
1394 int test_thread_support(void)
1395 {
1396   int result = 1 ;
1397   output_string("TESTING Thread support infrastructure\n") ;
1398   stub_pack_Tpkt_threadid(test_req) ;
1399   PKT_TRACE("packing the threadid  -> ",test_req) ;
1400   result &= currthread_test(&testthread) ;
1401   result &= get_and_display_threadinfo(&testthread) ;
1402   result &= threadlist_test() ;
1403   result &= setthread_test(&testthread) ;
1404   if (result)
1405     output_string("PASS: UNITTEST Thread support\n") ;
1406   else
1407         output_string("FAIL: UNITTEST Thread support\n") ;
1408   return result ;
1409 } /* test-thread_support */
1410 #endif /* UNIT_TEST */
1411
1412 // #ifdef __ECOS__
1413 #endif // ifdef CYGDBG_HAL_DEBUG_GDB_THREAD_SUPPORT...
1414 // #endif // __ECOS__