Mercurial > flash_v2
diff packages/hal/common/current/src/thread-packets.c @ 0:3111d98ba7b3 ecos-v1_1-release
Initial commit of eCos version 1.1
| author | jlarmour |
|---|---|
| date | Tue, 11 May 1999 11:16:07 +0000 |
| parents | |
| children | 443894e2e912 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/hal/common/current/src/thread-packets.c @@ -0,0 +1,1303 @@ + +/* + * Copyright (c) 1998 Cygnus Support + * + * The authors hereby grant permission to use, copy, modify, distribute, + * and license this software and its documentation for any purpose, provided + * that existing copyright notices are retained in all copies and that this + * notice is included verbatim in any distributions. No written agreement, + * license, or royalty fee is required for any of the authorized uses. + * Modifications to this software may be copyrighted by their authors + * and need not follow the licensing terms described here, provided that + * the new terms are clearly indicated on the first page of each file where + * they apply. + */ + +#include <pkgconf/hal.h> + +#if defined(CYGDBG_HAL_DEBUG_GDB_THREAD_SUPPORT) \ + && defined(CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS) + +/* FIXME: Scan this module for correct sizes of fields in packets */ +#define DEBUG_THREADS 1 +#include "cyg/hal/dbg-threads-api.h" + +/* This file should ALWAYS define debug thread support */ +/* Dont include the object in the link if you dont need the support */ +/* This is NOT the internal unit debug flag, it is a feature control */ +#if defined(DEBUG_THREADS) +#undef DEBUG_THREADS +#define DEBUG_THREADS 1 +#endif + +#define UNIT_TEST 0 +#define GDB_MOCKUP 0 + + +#define STUB_BUF_MAX 300 /* for range checking of packet lengths */ + +#include "thread-pkts.h" + +// Use HAL rather than board.h in eCos +#define PKT_DEBUG 0 +#include <cyg/hal/hal_stub.h> +//#include "board.h" + +#if !defined(PKT_DEBUG) +#define PKT_DEBUG 0 +#endif +extern void output_string(char * message) ; + +#if PKT_DEBUG +void output_threadid(char * title,threadref * ref) ; +#warning "PKT_DEBUG macros engaged" +#define PKT_TRACE(title,packet) \ +{ output_string(title) ; output_string(packet) ; output_string("\n") ;} +#else +#define PKT_TRACE(title,packet) {} +#endif + + +/* This is going to be irregular because the various implementations + have adopted different names for registers. + It would be nice to fix them to have a common convention + _stub_registers + stub_registers + alt_stub_registers + */ + +extern target_register_t * _registers ; + /* A pointer to the current set of registers */ +extern target_register_t registers[NUMREGS]; /* The current saved registers */ +extern target_register_t alt_registers[NUMREGS] ; + /* Thread or saved process state */ + + +void __stub_copy_registers( + target_register_t * dest, + target_register_t *src + ) +{ + target_register_t * limit ; + limit = dest + NUMREGS ; + + while (dest < limit) *dest++ = *src++ ; +} + +extern int stubhex(char ch) ; + +/* ----- STUB_PACK_NAK ----------------------------------- */ +/* Pack an error response into the response packet */ + +char * stub_pack_nak(char * outbuf) +{ + *outbuf++ = 'E' ; + *outbuf++ = '0' ; + *outbuf++ = '2' ; + return outbuf ; +} /* stub_pack_nak */ + +/* ----- STUB_PACK_ACK -------------------------- */ +/* Pack an OK achnowledgement */ +char * stub_pack_ack(char * outbuf) +{ + *outbuf++ = 'O' ; + *outbuf++ = 'K' ; + return outbuf ; +} /* stub_pack_ack */ + +/* ------- STUB_UNPACK_INT ------------------------------- */ +/* Unpack a few bytes and return its integer value */ +/* This is where I wish functions could return several values + I would also advance the buffer pointer */ + +int stub_unpack_int(char * buff,int fieldlength) +{ + int retval = 0 ; + int nibble ; + while (fieldlength) + { nibble = stubhex(*buff++) ; + retval |= nibble ; + fieldlength-- ; + if (fieldlength) retval = retval << 4 ; + } + return retval ; +} /* stub_unpack_int */ + +static char * unpack_byte(char * buf, int * value) +{ + *value = stub_unpack_int(buf,2) ; + return buf + 2 ; +} + +static char * unpack_int(char * buf, int * value) +{ + *value = stub_unpack_int(buf,8) ; + return buf + 8 ; +} + +/* We are NOT depending upun extensive libraries */ +static int ishex(char ch,int *val) +{ + if ((ch >= 'a') && (ch <= 'f')) + { *val =ch - 'a' + 10 ; return 1 ; } + if ((ch >= 'A') && (ch <= 'F')) + { *val = ch - 'A' + 10 ; return 1 ;} + if ((ch >= '0') && (ch <= '9')) + { *val = ch - '0' ; return 1 ; } + return 0 ; +} /* ishex */ + +static char * unpack_nibble(char * buf,int * val) +{ + ishex(*buf++,val) ; + return buf ; +} + + +static const char hexchars[] = "0123456789abcdef"; + +static char * pack_hex_byte(char * pkt, unsigned char byte) +{ + *pkt++ = hexchars[(byte >> 4) & 0xf] ; + *pkt++ = hexchars[(byte & 0xf)] ; + return pkt ; +} /* pack_hex_byte */ + +#if 0 // Currenly unused by eCos stub. +/* ---- STUB_PACK_VARLEN_HEX ------------------------------------- */ +/* Format a variable length stream of hex bytes */ + +static char * pack_varlen_hex( + char * pkt, + unsigned int value) +{ + int i ; + static unsigned char n[8] ; + if (value == 0) + { + *pkt++ = '0' ; + return pkt ; + } + else + { + i = 8 ; + while (i-- >= 0 ) /* unpack nibbles into a char array */ + { + n[i] = value & 0x0f ; + value = value >> 4 ; + } + i = 0 ; /* we had descrmented it to -1 */ + while (n[i] == 0 ) i++ ; /* drop leading zeroes */ + while (i++ < 8) *pkt++ = hexchars[n[i]] ; /* pack the number */ + } + return pkt ; +} /* pack_varlen_hex */ +#endif + +/* ------ STUB_UNPACK_VARLEN_HEX -------------------------------- */ +/* Parse a stream of hex bytes which may be of variable length */ +/* return the pointer to the next char */ +/* modify a varparm containing the result */ +/* A failure would look like a non-increment of the buffer pointer */ + +/* This unpacks hex strings that may have been packed using sprintf(%x) */ +/* We assume some non-hex delimits them */ + +char * unpack_varlen_hex( + char * buff, /* packet to parse */ + int * result) +{ + int nibble ; + int retval ; + retval = 0 ; + + while (ishex(*buff,&nibble)) + { + buff++ ; + retval = retval << 4 ; + retval |= nibble & 0x0f ; + } + *result = retval ; + return buff ; +} /* stub_unpack_varlen_int */ + + +/* ------ UNPACK_THREADID ------------------------------- */ +/* A threadid is a 64 bit quantity */ + +#define BUFTHREADIDSIZ 16 /* encode 64 bits in 16 chars of hex */ + +static char * unpack_threadid(char * inbuf, threadref * id) +{ + char * altref ; + char * limit = inbuf + BUFTHREADIDSIZ ; + int x,y ; + altref = (char *) id ; + + while (inbuf < limit) + { + x = stubhex(*inbuf++) ; + y = stubhex(*inbuf++) ; + *altref++ = (x << 4) | y ; + } + return inbuf ; +} /* unpack_threadid */ + + + +/* Pack an integer use leading zeroes */ +static char * pack_int(char * buf,int value) +{ + buf = pack_hex_byte(buf,(value>> 24)& 0xff) ; + buf = pack_hex_byte(buf,(value >>16)& 0xff) ; + buf = pack_hex_byte(buf,(value >>8) & 0x0ff) ; + buf = pack_hex_byte(buf,(value & 0xff)) ; + return buf ; +} /* pack_int */ + + +/* -------- PACK_STRING ---------------------------------------------- */ +/* This stupid string better not contain any funny characters */ +/* Also, the GDB protocol will not cope with NULLs in the + string or at the end of it. + While is is posable to encapsulate the protocol in ays that + preclude filtering for # I am assuming this is a constraint. +*/ + +static char * pack_raw_string(char * pkt,char * string) +{ + char ch ; + while (0 != (ch = *string++)) *pkt++ = ch ; + return pkt ; +} + +static char * pack_string( + char * pkt, + char * string) +{ + char ch ; + int len = 0; + char *s = string; + while( *s++ ) len++; + if (len > 200 ) len = 200 ; /* Bigger than most GDB packets, junk??? */ + pkt = pack_hex_byte(pkt,len) ; + while (len-- > 0) + { + ch = *string++ ; + if ((ch == '\0') || (ch == '#')) ch = '*' ; /* Protect encapsulation */ + *pkt++ = ch ; + } + return pkt ; +} /* pack_string */ + + +/* ----- STUB_PACK_THREADID --------------------------------------------- */ +/* Convert a binary 64 bit threadid and pack it into a xmit buffer */ +/* Return the advanced buffer pointer */ + +static char * pack_threadid(char * pkt, threadref * id) +{ + char * limit ; + unsigned char * altid ; + altid = (unsigned char *) id ; + limit = pkt + BUFTHREADIDSIZ ; + while (pkt < limit) pkt = pack_hex_byte(pkt,*altid++) ; + return pkt ; +} /* stub_pack_threadid */ + +/* UNFORTUNATLY, not all ow the extended debugging system has yet been + converted to 64 but thread referenecces and process identifiers. + These routines do the conversion. + An array of bytes is the correct treatment of an opaque identifier. + ints have endian issues. + */ + +static void int_to_threadref(threadref * id, int value) +{ + unsigned char * scan ; + scan = (unsigned char *) id ; + { + int i = 4 ; + while (i--) *scan++ = 0 ; + } + *scan++ = (value >> 24) & 0xff ; + *scan++ = (value >> 16) & 0xff ; + *scan++ = (value >> 8) & 0xff ; + *scan++ = (value & 0xff) ; +} + +static int threadref_to_int(threadref * ref) +{ + int value = 0 ; + unsigned char * scan ; + int i ; + + scan = (char *) ref ; + scan += 4 ; + i = 4 ; + while (i-- > 0) value = (value << 8) | ((*scan++) & 0xff) ; + return value ; +} /* threadref_to_int */ + +void copy_threadref(threadref * dest, threadref * src) +{ + int i ; + unsigned char * csrc, * cdest ; + csrc = (unsigned char *) src ; + cdest = (unsigned char *) dest ; + i = 8 ; + while (i--) *cdest++ = *csrc++ ; +} + + +int threadmatch( + threadref * dest , + threadref * src + ) +{ + unsigned char * srcp, * destp ; + int i , result ; + srcp = (char *) src ; + destp = (char *) dest ; + i = 8 ; + result = 1 ; + while (i-- > 0 ) result &= (*srcp++ == *destp++) ? 1 : 0 ; + return result ; +} /* threadmatch */ + + + +static char * Tpkt_threadtag = "thread:" ; + +/* ----- STUB_PACK_TPKT_THREADID ------------------------------------ */ +/* retreive, tag and insert a thread identifier into a T packet. */ +/* Insert nothing if the thread identifier is not available */ + +char * stub_pack_Tpkt_threadid(char * pkt) +{ + static threadref thread ; + int fmt = 0 ; /* old format */ + PKT_TRACE("Tpkt-id","---") ; + if (dbg_currthread(&thread)) + { + pkt = pack_raw_string(pkt,Tpkt_threadtag) ; + if (fmt) + pkt = pack_threadid(pkt,&thread) ; + else + /* Until GDB lengthens its thread ids, we have to MASH + the threadid into somthing shorter. PLEASE FIX GDB */ + pkt = pack_int(pkt,threadref_to_int(&thread)) ; + *pkt++ = ';' ; /* terminate variable length int */ + *pkt = '\0' ; /* Null terminate to allow string to be printed, no++ */ + } + PKT_TRACE("packedTpkt","--") ; + return pkt ; +} /* stub_pack_Tpkt_threadid */ + + +void stub_pkt_currthread( + char * inbuf, + char * outbuf, + int bufmax) +{ + threadref thread ; + char * base_out ; + base_out = outbuf ; + + if (dbg_currthread(&thread)) + { + *outbuf++ = 'q' ; + *outbuf++ = 'P' ; /* FIXME: Is this a reasanable code */ + outbuf = pack_threadid(outbuf,&thread) ; /* Long form */ + } + else outbuf = stub_pack_nak(outbuf) ; + *outbuf = '\0' ; /* terminate response packet */ + PKT_TRACE("stub_pkt_currthread(resp) ",base_out) ; +} /* stub_pkt_currthread */ + +/* ----- STUB_PKT_THREAD_ALIVE --------------------------------- */ +/* Answer the thread alive query */ + +void stub_pkt_thread_alive(char * inbuf, + char * outbuf, + int bufmax) +{ + char * prebuf = inbuf ; + int result ; + threadref thread ; + struct cygmon_thread_debug_info info ; + + if (prebuf != (inbuf = unpack_varlen_hex(inbuf,&result))) + { + int_to_threadref(&thread,result) ; + /* pack old format into internal format */ + if ((dbg_threadinfo(&thread,&info)) + && info.context_exists) + { outbuf = stub_pack_ack(outbuf) ; + *outbuf = '\0' ; + return ; + } + } + outbuf = stub_pack_nak(outbuf) ; + *outbuf = '\0' ; /* terminate the response message */ +} /* stub_pkt_thread_alive */ + + + +/* ----- STUB_PKT_CHANGETHREAD ------------------------------- */ +/* Switch the display of registers to that of a saved context */ + +/* Changing the context makes NO sense, although the packets define the + capability. Therefore, the option to change the context back does + call the function to change registers. Also, there is no + forced context switch. + 'p' - New format, long long threadid, no special cases + 'c' - Old format, id for continue,32 bit threadid max, possably less + -1 contimue means continue all threads + 'g' - Old Format, id for general use (other than continue) + + replies: + OK for success + ENN for error + */ + +void stub_pkt_changethread( + char * inbuf, + char * outbuf, + int bufmax) +{ + threadref id ; + int idefined = -1 ; + char ch ; + PKT_TRACE("setthread-pkt ",inbuf) ; + + /* Parse the incoming packet for a thread identifier */ + switch (ch = *inbuf++ ) /* handle various packet formats */ + { + case 'p' : /* New format: mode:8,threadid:64 */ + inbuf = unpack_nibble(inbuf,&idefined) ; + inbuf = unpack_threadid(inbuf,&id) ; /* even if startflag */ + break ; + case 'c' : /* old format , specify thread for continue */ + outbuf = stub_pack_nak(outbuf) ; /* Not supported */ + break ; + case 'g' : /* old format, specify thread for general operations */ + /* OLD format: parse a variable length hex string */ + /* OLD format consider special thread ids */ + { + int oldthreadid ; + inbuf = unpack_varlen_hex(inbuf,&oldthreadid) ; + int_to_threadref(&id,oldthreadid) ; + switch (oldthreadid) + { + case 0 : /* pick a thread, any thread */ + idefined = 2 ; /* select original interrupted context */ + break ; + case -1 : /* all threads */ + idefined = 2 ; + break ; + default : + idefined = 1 ; /* select the specified thread */ + break ; + } + } + break ; + default: + outbuf = stub_pack_nak(outbuf) ; + break ; + } /* handle various packet formats */ + + + switch (idefined) + { + case -1 : + /* Packet not supported, already NAKed, no further action */ + break ; + case 0 : + /* Switch back to interrupted context */ + _registers = ®isters[0] ; + break ; + case 1 : + /* copy the saved registers into the backup registers */ + __stub_copy_registers(alt_registers,registers) ; + /* The OS will now update the values it has in a saved process context*/ + if (dbg_getthreadreg(&id,NUMREGS,&alt_registers[0])) + { + /* switch the registers pointer */ + _registers = &alt_registers[0] ; + outbuf = stub_pack_ack(outbuf) ; + } + else + outbuf = stub_pack_nak(outbuf) ; + break ; + case 2 : break ; /* switch to interrupted context */ + default: + outbuf = stub_pack_nak(outbuf) ; + break ; + } + *outbuf = '\0' ; /* Terminate response pkt */ +} /* stub_pkt-changethread */ + + +/* ---- STUB_PKT_GETTHREADLIST ------------------------------- */ +/* Get a portion of the threadlist or process list */ +/* This may be part of a multipacket transaction */ +/* It would be hard to tell in the response packet the difference + between the end of list and an error in threadid encoding. + */ + +void stub_pkt_getthreadlist(char * inbuf, + char * outbuf, + int bufmax) +{ + char * count_ptr ; + char * done_ptr ; + char * limit ; + int start_flag , batchsize , result , count ; + static threadref lastthread, nextthread ; + +#if PKT_DEBUG + char * r_base = outbuf ; +#endif + PKT_TRACE("pkt_getthreadlist: ",inbuf) ; + + count = 0 ; + inbuf = unpack_nibble(inbuf,&start_flag) ; + inbuf = unpack_byte(inbuf,&batchsize) ; + inbuf = unpack_threadid(inbuf,&lastthread) ; /* even if startflag */ + + /* Start building response packet */ + limit = outbuf + (bufmax - BUFTHREADIDSIZ - 10) ; /* id output packing limit */ + *outbuf++ = 'Q' ; + *outbuf++ = 'M' ; + + /* Default values for count and done fields, save ptr to repatch */ + count_ptr = outbuf ; /* save to repatch count */ + outbuf = pack_hex_byte(outbuf,0) ; + done_ptr = outbuf ; /* Backpatched later */ + *outbuf++ = '0' ; /* Done = 0 by default */ + outbuf = pack_threadid(outbuf,&lastthread) ; + + /* Loop through the threadid packing */ + while ((outbuf < limit) && (count < batchsize)) + { + result = dbg_threadlist(start_flag,&lastthread,&nextthread) ; + start_flag = 0 ; /* redundent but effective */ + if (!result) + { *done_ptr = '1' ; /* pack the done flag */ + break ; + } +#if 0 /* DEBUG */ + if (threadmatch(&lastthread,&nextthread)) + { + output_string("FAIL: Threadlist, not incrementing\n") ; + *done_ptr = '1' ; + break ; + } +#endif + count++ ; + outbuf = pack_threadid(outbuf,&nextthread) ; + copy_threadref(&lastthread,&nextthread) ; + } + pack_hex_byte(count_ptr,count) ;/* backpatch, Pack the count field */ + *outbuf = '\0' ; + PKT_TRACE("pkt_getthreadlist(resp) ",r_base) ; +} /* pkt_getthreadlist */ + + + + +/* ----- STUB_PKT_GETTHREADINFO ---------------------------------------- */ +/* Get the detailed information about a specific thread or process */ + +/* +Encoding: + 'Q':8,'P':8,mask:16 + + Mask Fields + threadid:1, # always request threadid + context_exists:2, + display:4, + unique_name:8, + more_display:16 + */ + +void stub_pkt_getthreadinfo( + char * inbuf, + char * outbuf, + int bufmax) +{ + int mask ; + int result ; + threadref thread ; + struct cygmon_thread_debug_info info ; + + info.context_exists = 0 ; + info.thread_display = 0 ; + info.unique_thread_name = 0 ; + info.more_display = 0 ; + + /* Assume the packet identification chars have already been + discarded by the packet demultiples routines */ + PKT_TRACE("PKT getthreadinfo",inbuf) ; + + inbuf = unpack_int(inbuf,&mask) ; + inbuf = unpack_threadid(inbuf,&thread) ; + + result = dbg_threadinfo(&thread,&info) ; /* Make system call */ + + if (result) + { + *outbuf++ = 'q' ; + *outbuf++ = 'p' ; + outbuf = pack_int(outbuf,mask) ; + outbuf = pack_threadid(outbuf,&info.thread_id) ; /* echo threadid */ + if (mask & 2) /* context-exists */ + { + outbuf = pack_int(outbuf,2) ; /* tag */ + outbuf = pack_hex_byte(outbuf,2) ; /* length */ + outbuf = pack_hex_byte(outbuf,info.context_exists) ; + } + if ((mask & 4) && info.thread_display)/* display */ + { + outbuf = pack_int(outbuf,4) ; /* tag */ + outbuf = pack_string(outbuf,info.thread_display) ; + } + if ((mask & 8) && info.unique_thread_name) /* unique_name */ + { + outbuf = pack_int(outbuf,8) ; + outbuf = pack_string(outbuf,info.unique_thread_name) ; + } + if ((mask & 16) && info.more_display) /* more display */ + { + outbuf = pack_int(outbuf,16) ; /* tag 16 */ + outbuf = pack_string(outbuf,info.more_display) ; + } + } + else + { + PKT_TRACE("FAIL: dbg_threadinfo\n", "") ; + outbuf = stub_pack_nak(outbuf) ; + } + *outbuf = '\0' ; +} /* stub_pkt_getthreadinfo */ + + + + +#if GDB_MOCKUP +/* ------ MATCHED GDB SIDE PACKET ENCODING AND PARSING ----------------- */ + +char * pack_nibble(char * buf, int nibble) +{ + *buf++ = hexchars[(nibble & 0x0f)] ; + return buf ; +} /* pack_nibble */ + +#if 0 +static char * unpack_short(char * buf,int * value) +{ + *value = stub_unpack_int(buf,4) ; + return buf + 4 ; +} + +static char * pack_short( + char * buf, + unsigned int value) +{ + buf = pack_hex_byte(buf,(value >> 8) & 0xff) ; + buf = pack_hex_byte(buf,(value & 0xff)) ; + return buf ; +} /* pack_short */ +#endif + + +/* Generally, I dont bury xmit and receive calls inside packet formatters + and parsers + */ + + + + +/* ----- PACK_SETTHREAD_REQUEST ------------------------------------- */ +/* Encoding: ??? decode gdb/remote.c + 'Q':8,'p':8,idefined:8,threadid:32 ; + */ + +char * pack_setthread_request( + char * buf, + char fmt, /* c,g or, p */ + int idformat , + threadref * threadid ) +{ + *buf++ = fmt ; + + if (fmt == 'p') + { /* pack the long form */ + buf = pack_nibble(buf,idformat) ; + buf = pack_threadid(buf,threadid) ; + } + else + { /* pack the shorter form - Serious truncation */ + /* There are reserved identifieds 0 , -1 */ + int quickref = threadref_to_int(threadid) ; + buf = pack_varlen_hex(buf,quickref) ; + } + *buf++ = '\0' ; /* end_of_packet */ + return buf ; +} /* pack_setthread_request */ + + + +/* -------- PACK_THREADLIST-REQUEST --------------------------------- */ +/* Format: i'Q':8,i"L":8,initflag:8,batchsize:16,lastthreadid:32 */ + + +char * pack_threadlist_request( + char * pkt, + int startflag, + int threadcount, + threadref * nextthread + ) +{ + *pkt++ = 'q' ; + *pkt++ = 'L' ; + pkt = pack_nibble(pkt,startflag) ; /* initflag 1 bytes */ + pkt = pack_hex_byte(pkt,threadcount) ; /* threadcount 2 bytes */ + pkt = pack_threadid(pkt,nextthread) ; /* 64 bit thread identifier */ + *pkt = '\0' ; + return pkt ; +} /* remote_threadlist_request */ + + + + +/* ---------- PARSE_THREADLIST_RESPONSE ------------------------------------ */ +/* Encoding: 'q':8,'M':8,count:16,done:8,argthreadid:64,(threadid:64)* */ + +int parse_threadlist_response( + char * pkt, + threadref * original_echo, + threadref * resultlist, + int * doneflag) +{ + char * limit ; + int count, resultcount , done ; + resultcount = 0 ; + + /* assume the 'q' and 'M chars have been stripped */ + PKT_TRACE("parse-threadlist-response ",pkt) ; + limit = pkt + (STUB_BUF_MAX - BUFTHREADIDSIZ) ; /* done parse past here */ + pkt = unpack_byte(pkt,&count) ; ; /* count field */ + pkt = unpack_nibble(pkt,&done) ; + /* The first threadid is the argument threadid */ + pkt = unpack_threadid(pkt,original_echo) ; /* should match query packet */ + while ((count-- > 0) && (pkt < limit)) + { + pkt = unpack_threadid(pkt,resultlist++) ; + resultcount++ ; + } + if (doneflag) *doneflag = done ; + return resultcount ; /* successvalue */ +} /* parse_threadlist_response */ + +struct gdb_ext_thread_info +{ + threadref threadid ; + int active ; + char display[256] ; + char shortname[32] ; + char more_display[256] ; +} ; + + +/* ----- PACK_THREAD_INFO_REQUEST -------------------------------- */ + +/* + threadid:1, # always request threadid + context_exists:2, + display:4, + unique_name:8, + more_display:16 + */ + +/* Encoding: 'Q':8,'P':8,mask:32,threadid:64 */ + +char * pack_threadinfo_request(char * pkt, + int mode, + threadref * id + ) +{ + *pkt++ = 'Q' ; + *pkt++ = 'P' ; + pkt = pack_int(pkt,mode) ; /* mode */ + pkt = pack_threadid(pkt,id) ; /* threadid */ + *pkt = '\0' ; /* terminate */ + return pkt ; +} /* pack_thread_info_request */ + + + +static char * unpack_string( + char * src, + char * dest, + int length) +{ + while (length--) *dest++ = *src++ ; + *dest = '\0' ; + return src ; +} /* unpack_string */ + + +void output_threadid(char * title,threadref * ref) +{ + char hexid[20] ; + pack_threadid(&hexid[0],ref) ; /* Convert threead id into hex */ + hexid[16] = 0 ; + output_string(title) ; output_string(&hexid[0]) ; output_string("\n"); +} + +/* ------ REMOTE_UPK_THREAD_INFO_RESPONSE ------------------------------- */ +/* Unpack the response of a detailed thread info packet */ +/* Encoding: i'Q':8,i'R':8,argmask:16,threadid:64,(tag:8,length:16,data:x)* */ + +#define TAG_THREADID 1 +#define TAG_EXISTS 2 +#define TAG_DISPLAY 4 +#define TAG_THREADNAME 8 +#define TAG_MOREDISPLAY 16 + + +int remote_upk_thread_info_response( + char * pkt, + threadref * expectedref , + struct gdb_ext_thread_info * info) +{ + int mask, length ; + unsigned int tag ; + threadref ref ; + char * limit = pkt + 500 ; /* plausable parsing limit */ + int retval = 1 ; + + PKT_TRACE("upk-threadinfo ",pkt) ; + + /* info->threadid = 0 ; FIXME: implement zero_threadref */ + info->active = 0 ; + info->display[0] = '\0' ; + info->shortname[0] = '\0' ; + info->more_display[0] = '\0' ; + + /* Assume the characters indicating the packet type have been stripped */ + pkt = unpack_int(pkt,&mask) ; /* arg mask */ + pkt = unpack_threadid(pkt , &ref) ; + + if (! threadmatch(&ref,expectedref)) + { /* This is an answer to a different request */ + output_string("FAIL Thread mismatch\n") ; + output_threadid("ref ",&ref) ; + output_threadid("expected ",expectedref) ; + return 0 ; + } + copy_threadref(&info->threadid,&ref) ; + + /* Loop on tagged fields , try to bail if somthing goes wrong */ + if (mask==0) output_string("OOPS NO MASK \n") ; + + while ((pkt < limit) && mask && *pkt) /* packets are terminated with nulls */ + { + pkt = unpack_int(pkt,&tag) ; /* tag */ + pkt = unpack_byte(pkt,&length) ; /* length */ + if (! (tag & mask)) /* tags out of synch with mask */ + { + output_string("FAIL: threadinfo tag mismatch\n") ; + retval = 0 ; + break ; + } + if (tag == TAG_THREADID) + { + output_string("unpack THREADID\n") ; + if (length != 16) + { + output_string("FAIL: length of threadid is not 16\n") ; + retval = 0 ; + break ; + } + pkt = unpack_threadid(pkt,&ref) ; + mask = mask & ~ TAG_THREADID ; + continue ; + } + if (tag == TAG_EXISTS) + { + info->active = stub_unpack_int(pkt,length) ; + pkt += length ; + mask = mask & ~(TAG_EXISTS) ; + if (length > 8) + { + output_string("FAIL: 'exists' length too long\n"); + retval = 0 ; + break ; + } + continue ; + } + if (tag == TAG_THREADNAME) + { + pkt = unpack_string(pkt,&info->shortname[0],length) ; + mask = mask & ~TAG_THREADNAME ; + continue ; + } + if (tag == TAG_DISPLAY) + { + pkt = unpack_string(pkt,&info->display[0],length) ; + mask = mask & ~TAG_DISPLAY ; + continue ; + } + if (tag == TAG_MOREDISPLAY) + { + pkt = unpack_string(pkt,&info->more_display[0],length) ; + mask = mask & ~TAG_MOREDISPLAY ; + continue ; + } + output_string("FAIL: unknown info tag\n") ; + break ; /* Not a tag we know about */ + } + return retval ; +} /* parse-thread_info_response */ + + +/* ---- REMOTE_PACK-CURRTHREAD_REQUEST ---------------------------- */ +/* This is a request to emit the T packet */ + +/* FORMAT: 'q':8,'C' */ + +char * remote_pack_currthread_request(char * pkt ) +{ + *pkt++ = 'q' ; + *pkt++ = 'C' ; + *pkt = '\0' ; + return pkt ; +} /* remote_pack-currthread_request */ + + +/* ------- REMOTE_UPK_CURTHREAD_RESPONSE ----------------------- */ +/* Unpack the interesting part of a T packet */ + + +int remote_upk_currthread_response( + char * pkt, + threadref * ref ) /* Parse a T packet */ +{ + int retval = 0 ; + PKT_TRACE("upk-currthreadresp ",pkt) ; + +#if 0 + { + static char threadtag[8] = "thread" ; + int retval = 0 ; + int i , found ; + char ch ; + int quickid ; + + /* Unpack as a t packet */ + while (((ch = *pkt++) != ':') /* scan for : thread */ + && (ch != '\0')) /* stop at end of packet */ + + { + found = 0 ; + i = 0 ; + while ((ch = *pkt++) == threadtag[i++]) ; + if (i == 8) /* string match "thread" */ + { + pkt = unpack_varlen_hex(pkt,&quickid) ; + retval = 1; + break ; + } + retval = 0 ; + } + } +#else + pkt = unpack_threadid(pkt,ref) ; + retval = 1 ; +#endif + return retval ; +} /* remote_upk_currthread_response */ + + +/* -------- REMOTE_UPK-SIMPLE_ACK --------------------------------- */ +/* Decode a response which is eother "OK" or "Enn" + fillin error code, fillin pkfag-1== undef, 0==nak, 1 == ack ; + return advanced packet pointer */ + + +char * remote_upk_simple_ack( + char * buf, + int * pkflag, + int * errcode) +{ + int lclerr = 0 ; + char ch = *buf++ ; + int retval = -1 ; /* Undefined ACK , a protocol error */ + if (ch == 'E') /* NAK */ + { + buf = unpack_byte(buf,&lclerr) ; + retval = 0 ; /* transaction failed, explicitly */ + } + else + if ((ch == 'O') && (*buf++ == 'K')) /* ACK */ + retval = 1 ; /* transaction succeeded */ + *pkflag = retval ; + *errcode = lclerr ; + return buf ; +} /* remote-upk_simple_ack */ + + +/* -------- PACK_THREADALIVE_REQUEST ------------------------------- */ + +char * pack_threadalive_request( + char * buf, + threadref * threadid) +{ + *buf++ = 'T' ; + buf = pack_threadid(buf,threadid) ; + *buf = '\0' ; + return buf ; +} /* pack_threadalive_request */ + +#endif /* GDB_MOCKUP */ + +/* ---------------------------------------------------------------------- */ +/* UNIT_TESTS SUBSECTION */ +/* ---------------------------------------------------------------------- */ + + +#if UNIT_TEST +extern void output_string(char * message) ; +static char test_req[400] ; +static char t_response[400] ; + + + +/* ----- DISPLAY_THREAD_INFO ---------------------------------------------- */ +/* Use local cygmon string output utiities */ + +void display_thread_info(struct gdb_ext_thread_info * info) +{ + + output_threadid("Threadid: ",&info->threadid) ; + /* short name */ + output_string("Name: ") ; output_string(info->shortname) ; output_string("\n"); + /* format display state */ + output_string("State: ") ; output_string(info->display) ; output_string("\n") ; + /* additional data */ + output_string("other: ");output_string(info->more_display); + output_string("\n\n"); +} /* display_thread_info */ + + +/* --- CURRTHREAD-TEST -------------------------------------------- */ +static int currthread_test(threadref * thread) +{ + int result ; + output_string("TEST: currthread\n") ; + remote_pack_currthread_request(test_req) ; + stub_pkt_currthread(test_req+2,t_response,STUB_BUF_MAX) ; + result = remote_upk_currthread_response(t_response+2,thread) ; + if (result) + { + output_string("PASS getcurthread\n") ; + /* FIXME: print the thread */ + } + else + output_string("FAIL getcurrthread\n") ; + return result ; +} /* currthread_test */ + +/* ------ SETTHREAD_TEST ------------------------------------------- */ + /* use a known thread from previous test */ + +static int setthread_test(threadref * thread) +{ + int result, errcode ; + output_string("TEST: setthread\n") ; + + pack_setthread_request(test_req,'p',1,thread) ; + stub_pkt_changethread(test_req,t_response,STUB_BUF_MAX) ; + remote_upk_simple_ack(t_response,&result,&errcode) ; + switch (result) + { + case 0 : + output_string("FAIL setthread\n") ; + break ; + case 1 : + output_string("PASS setthread\n") ; + break ; + default : + output_string("FAIL setthread -unrecognized response\n") ; + break ; + } + return result ; +} /* setthread_test */ + + +/* ------ THREADACTIVE_TEST ---------------------- */ + /* use known thread */ + /* pack threadactive packet */ + /* process threadactive packet */ + /* parse threadactive response */ + /* check results */ + + +int threadactive_test(threadref * thread) +{ + int result ; + int errcode ; + output_string("TEST: threadactive\n") ; + pack_threadalive_request(test_req,thread) ; + stub_pkt_thread_alive(test_req+1,t_response,STUB_BUF_MAX); + remote_upk_simple_ack(t_response,&result,&errcode) ; + switch (result) + { + case 0 : + output_string("FAIL threadalive\n") ; + break ; + case 1 : + output_string("PASS threadalive\n") ; + break ; + default : + output_string("FAIL threadalive -unrecognized response\n") ; + break ; + } + return result ; +} /* threadactive_test */ + +/* ------ REMOTE_GET_THREADINFO -------------------------------------- */ +int remote_get_threadinfo( + threadref * threadid, + int fieldset , /* TAG mask */ + struct gdb_ext_thread_info * info + ) +{ + int result ; + pack_threadinfo_request(test_req,fieldset,threadid) ; + stub_pkt_getthreadinfo(test_req+2,t_response,STUB_BUF_MAX) ; + result = remote_upk_thread_info_response(t_response+2,threadid,info) ; + return result ; +} /* remote_get-thrreadinfo */ + + +static struct gdb_ext_thread_info test_info ; + +static int get_and_display_threadinfo(threadref * thread) +{ + int mode ; + int result ; + /* output_string("TEST: get and display threadinfo\n") ; */ + + mode = TAG_THREADID | TAG_EXISTS | TAG_THREADNAME + | TAG_MOREDISPLAY | TAG_DISPLAY ; + result = remote_get_threadinfo(thread,mode,&test_info) ; + if (result) display_thread_info(&test_info) ; +#if 0 /* silent subtest */ + if (result) + output_string("PASS: get_and_display threadinfo\n") ; + else + output_string("FAIL: get_and_display threadinfo\n") ; +#endif + return result ; +} /* get-and-display-threadinfo */ + + + +/* ----- THREADLIST_TEST ------------------------------------------ */ +#define TESTLISTSIZE 16 +#define TLRSIZ 2 +static threadref test_threadlist[TESTLISTSIZE] ; + +static int threadlist_test(void) +{ + int done, i , result_count ; + int startflag = 1 ; + int result = 1 ; + int loopcount = 0 ; + static threadref nextthread ; + static threadref echo_nextthread ; + + output_string("TEST: threadlist\n") ; + + done = 0 ; + while (! done) + { + if (loopcount++ > 10) + { + result = 0 ; + output_string("FAIL: Threadlist test -infinite loop-\n") ; + break ; + } + pack_threadlist_request(test_req,startflag,TLRSIZ,&nextthread) ; + startflag = 0 ; /* clear for later iterations */ + stub_pkt_getthreadlist(test_req+2,t_response,STUB_BUF_MAX); + result_count = parse_threadlist_response(t_response+2, + &echo_nextthread, + &test_threadlist[0],&done) ; + if (! threadmatch(&echo_nextthread,&nextthread)) + { + output_string("FAIL: threadlist did not echo arg thread\n"); + result = 0 ; + break ; + } + if (result_count <= 0) + { + if (done != 0) + { output_string("FAIL threadlist_test, failed to get list"); + result = 0 ; + } + break ; + } + if (result_count > TLRSIZ) + { + output_string("FAIL: threadlist response longer than requested\n") ; + result = 0 ; + break ; + } + /* Setup to resume next batch of thread references , set nestthread */ + copy_threadref(&nextthread,&test_threadlist[result_count-1]) ; + /* output_threadid("last-of-batch",&nextthread) ; */ + i = 0 ; + while (result_count--) + { + if (0) /* two display alternatives */ + output_threadid("truncatedisplay",&test_threadlist[i++]) ; + else + get_and_display_threadinfo(&test_threadlist[i++]) ; + } + + } + if (!result) + output_string("FAIL: threadlist test\n") ; + else output_string("PASS: Threadlist test\n") ; + return result ; +} /* threadlist_test */ + + +static threadref testthread ; + + +int test_thread_support(void) +{ + int result = 1 ; + output_string("TESTING Thread support infrastructure\n") ; + stub_pack_Tpkt_threadid(test_req) ; + PKT_TRACE("packing the threadid -> ",test_req) ; + result &= currthread_test(&testthread) ; + result &= get_and_display_threadinfo(&testthread) ; + result &= threadlist_test() ; + result &= setthread_test(&testthread) ; + if (result) + output_string("PASS: UNITTEST Thread support\n") ; + else + output_string("FAIL: UNITTEST Thread support\n") ; + return result ; +} /* test-thread_support */ +#endif /* UNIT_TEST */ + +#endif // ifdef CYGDBG_HAL_DEBUG_GDB_THREAD_SUPPORT...
