minor
[charm.git] / src / conv-ccs / ccs-builtins.C
1 /*****************************************************************************
2  * A few useful built-in CCS handlers.
3  *****************************************************************************/
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <errno.h>
8 #include <string.h>
9
10 #include "converse.h"
11 #include "ckhashtable.h"
12 #include "pup.h"
13 #include "pup_toNetwork.h"
14 #include "debug-conv++.h"
15 #include "conv-ccs.h"
16 #include "sockRoutines.h"
17 #include "queueing.h"
18 #include "ccs-builtins.h"
19
20 #ifdef __MINGW_H
21 #include "process.h"
22 #endif
23
24 #if CMK_CCS_AVAILABLE
25
26 void ccs_getinfo(char *msg);
27
28 /**********************************************
29   "ccs_killport"-- takes one 4-byte big-endian port number
30     Register a "client kill port".  When this program exits,
31     it will connect to this TCP port and write "die\n\0" it.
32 */
33
34 typedef struct killPortStruct{
35   skt_ip_t ip;
36   unsigned int port;
37   struct killPortStruct *next;
38 } killPortStruct;
39 /*Only 1 kill list per node-- no Cpv needed*/
40 static killPortStruct *killList=NULL;
41
42 static void ccs_killport(char *msg)
43 {
44   killPortStruct *oldList=killList;
45   int port=ChMessageInt(*(ChMessageInt_t *)(msg+CmiReservedHeaderSize));
46   skt_ip_t ip;
47   unsigned int connPort;
48   CcsCallerId(&ip,&connPort);
49   killList=(killPortStruct *)malloc(sizeof(killPortStruct));
50   killList->ip=ip;
51   killList->port=port;
52   killList->next=oldList;
53   CmiFree(msg);
54 }
55 /*Send any registered clients kill messages before we exit*/
56 static int noMoreErrors(int c,const char *m) {return -1;}
57 extern "C" void CcsImpl_kill(void)
58 {
59   skt_set_abort(noMoreErrors);
60   while (killList!=NULL)
61   {
62     SOCKET fd=skt_connect(killList->ip,killList->port,20);
63     if (fd!=INVALID_SOCKET) {
64       skt_sendN(fd,"die\n",strlen("die\n")+1);
65       skt_close(fd);
66     }
67     killList=killList->next;
68   }
69 }
70
71 /**********************************************
72   "ccs_killpe"-- kills the executing processor
73     Used for fault-tolerance testing: terminate the processor.
74 */
75
76 #include <signal.h>
77
78 static void ccs_killpe(char *msg) {
79 #if CMK_HAS_KILL
80   kill(getpid(), 9);
81 #else
82   CmiAbort("ccs_killpe() not supported!");
83 #endif
84 }
85
86 /*************************************************
87 List interface:
88    This lets different parts of a Charm++ program register 
89 "list retrieval functions", which expose some aspect of a
90 running program.  Example lists include: all readonly globals, 
91 messages waiting in a queue, or all extant array elements.
92
93   "ccs_list_len" (4-byte character count, null-terminated ASCII string)
94 Return the number of items currently in the list at the given path.
95
96   "ccs_list_items.bin" (4-byte start, 4-byte end+1,4-byte extra
97 data count n, n-byte extra data, 4-byte character count c, c-byte
98 ASCII request string [without NULL terminator])
99 Return the given items (from start to end) of the given queue, 
100 formatted as raw network binary data.
101
102   "ccs_list_items.fmt" [parameters as for .bin]
103 Return the given items, formatted as tagged network binary data.
104
105   "ccs_list_items.txt" [parameters as for .bin]
106 Return the given items, formatted as ASCII text.
107 */
108
109 static void CpdListBoundsCheck(CpdListAccessor *l,int &lo,int &hi)
110 {
111   if (l->checkBoundary()) {
112     int len=l->getLength();
113     if (lo<0) lo=0;
114     if (hi>len) hi=len;
115   }
116 }
117
118 typedef CkHashtableTslow<const char *,CpdListAccessor *> CpdListTable_t;
119 CpvStaticDeclare(CpdListTable_t *,cpdListTable);
120
121 /**
122   Return the list at this (null-terminated ASCII) path.
123 */
124 static CpdListAccessor *CpdListLookup(const char *path)
125 {
126   CpdListAccessor *acc=CpvAccess(cpdListTable)->get(path);
127   if (acc==NULL) {
128     CmiError("CpdListAccessor> Unrecognized list path '%s'\n",path);
129     return NULL;
130   }
131   return acc;
132 }
133
134 /**
135   Return a CpdListAccessor, given a network string containing the 
136   list path.  A network string is a big-endian 32-bit "length" 
137   field, followed by a null-terminated ASCII string of that length.
138 */
139 static CpdListAccessor *CpdListLookup(const ChMessageInt_t *lenAndPath)
140 {
141   static const int CpdListMaxLen=80;
142   int len=ChMessageInt(lenAndPath[0]);
143   const char *path=(const char *)(lenAndPath+1);
144   char pathBuf[CpdListMaxLen+1]; //Temporary null-termination buffer
145   if ((len<0) || (len>CpdListMaxLen)) {
146     CmiError("CpdListAccessor> Invalid list path length %d!\n",len);
147     return NULL; //Character count is invalid
148   }
149   strncpy(pathBuf,path,len);
150   pathBuf[len]=0; //Ensure string is null-terminated
151   return CpdListLookup(pathBuf);
152 }
153
154 //CCS External access routines:
155
156 //Get the length of the given list:
157 static void CpdList_ccs_list_len(char *msg)
158 {
159   const ChMessageInt_t *req=(const ChMessageInt_t *)(msg+CmiReservedHeaderSize);
160   CpdListAccessor *acc=CpdListLookup(req);
161   if (acc!=NULL) {
162     ChMessageInt_t reply=ChMessageInt_new(acc->getLength());
163     CcsSendReply(sizeof(reply),(void *)&reply);
164   }
165   CmiFree(msg);
166 }
167
168 //Read a list contents request header:
169 //  first item to send, 4-byte network integer
170 //  last item+1 to send, 4-byte network integer
171 //  extra data length, 4-byte network integer
172 //  extra data, list-defined bytes
173 //  list path length, 4-byte network integer (character count)
174 //  list path name, null-terminated ASCII
175 static CpdListAccessor *CpdListHeader_ccs_list_items(char *msg,
176              CpdListItemsRequest &h)
177 {
178   int msgLen=CmiSize((void *)msg)-CmiReservedHeaderSize;
179   CpdListAccessor *ret=NULL;
180   const ChMessageInt_t *req=(const ChMessageInt_t *)(msg+CmiReservedHeaderSize);
181   h.lo=ChMessageInt(req[0]); // first item to send
182   h.hi=ChMessageInt(req[1]); // last item to send+1
183   h.extraLen=ChMessageInt(req[2]); // extra data length
184   if (h.extraLen>=0 
185   && ((int)(3*sizeof(ChMessageInt_t)+h.extraLen))<msgLen) {
186     h.extra=(void *)(req+3);  // extra data
187     ret=CpdListLookup((ChMessageInt_t *)(h.extraLen+(char *)h.extra));
188     if (ret!=NULL) CpdListBoundsCheck(ret,h.lo,h.hi);
189   }
190   return ret;
191 }
192
193
194 // Pup this cpd list's items under this request.
195 static void pupCpd(PUP::er &p, CpdListAccessor *acc, CpdListItemsRequest &req)
196 {
197       p.syncComment(PUP::sync_begin_array,"CpdList");
198       acc->pup(p,req);
199       p.syncComment(PUP::sync_end_array);
200 }
201
202 static void CpdList_ccs_list_items_txt(char *msg)
203 {
204   CpdListItemsRequest req;
205   CpdListAccessor *acc=CpdListHeader_ccs_list_items(msg,req);
206   if(acc == NULL) CmiPrintf("ccs-builtins> Null Accessor--bad list name (txt)\n");
207   if (acc!=NULL) {
208     int bufLen;
209     { 
210       PUP::sizerText p; pupCpd(p,acc,req); bufLen=p.size(); 
211     }
212     char *buf=new char[bufLen];
213     { 
214       PUP::toText p(buf); pupCpd(p,acc,req);
215       if (p.size()!=bufLen)
216         CmiError("ERROR! Sizing/packing length mismatch for %s list pup function!\n",
217                 acc->getPath());
218     }
219     CcsSendReply(bufLen,(void *)buf);
220     delete[] buf;
221   }
222   CmiFree(msg);
223 }
224
225 static void CpdList_ccs_list_items_set(char *msg)
226 {
227   CpdListItemsRequest req;
228   CpdListAccessor *acc=CpdListHeader_ccs_list_items(msg,req);
229   if(acc == NULL) CmiPrintf("ccs-builtins> Null Accessor--bad list name (set)\n");
230   else {
231     PUP_toNetwork_unpack p(req.extra);
232     pupCpd(p,acc,req);
233     if (p.size()!=req.extraLen)
234         CmiPrintf("Size mismatch during ccs_list_items.set: client sent %d bytes, but %d bytes used!\n",
235                 req.extraLen,p.size());
236   }
237   CmiFree(msg);
238 }
239
240 /** gather information about the machine we're currently running on */
241 void CpdMachineArchitecture(char *msg) {
242   char reply[8]; // where we store our reply
243   reply[0]=CHARMDEBUG_MAJOR;
244   reply[1]=CHARMDEBUG_MINOR;
245   // decide if we are 32 bit (1) or 64 bit (2)
246   reply[2] = 0;
247   if (sizeof(char*) == 4) reply[2] = 1;
248   else if (sizeof(char*) == 8) reply[2] = 2;
249   // decide if we are little endian (1) or big endian (2)
250   reply[3] = 0;
251   int value = 1;
252   char firstByte = *((char*)&value);
253   if (firstByte == 1) reply[3] = 1;
254   else reply[3] = 2;
255   // add the third bit if we are in bigsim
256 #if CMK_BIGSIM_CHARM
257   reply[3] |= 4;
258 #endif
259   // get the size of an "int"
260   reply[4] = sizeof(int);
261   // get the size of an "long"
262   reply[5] = sizeof(long);
263 #if CMK_LONG_LONG_DEFINED
264   // get the size of an "long long"
265   reply[6] = sizeof(long long);
266 #else
267   // Per Filippo, the debugger will be fine with this. It should never
268   // come up, since configure didn't detect support for `long long` on
269   // the machine.
270   reply[6] = 0;
271 #endif
272   // get the size of an "bool"
273   reply[7] = sizeof(bool);
274   CcsSendReply(8, (void*)reply);
275   CmiFree(msg);
276 }
277
278 static void CpdList_ccs_list_items_fmt(char *msg)
279 {
280   CpdListItemsRequest req;
281   CpdListAccessor *acc=CpdListHeader_ccs_list_items(msg,req);
282   if (acc!=NULL) {
283     int bufLen;
284     { 
285       PUP_toNetwork_sizer ps;
286       PUP_fmt p(ps); 
287       pupCpd(p,acc,req);
288       bufLen=ps.size(); 
289     }
290     char *buf=new char[bufLen];
291     { 
292       PUP_toNetwork_pack pp(buf); 
293       PUP_fmt p(pp);
294       pupCpd(p,acc,req);
295       if (pp.size()!=bufLen)
296         CmiError("ERROR! Sizing/packing length mismatch for %s list pup function (%d sizing, %d packing)\n",
297                 acc->getPath(),bufLen,pp.size());
298     }
299     CcsSendReply(bufLen,(void *)buf);
300     delete[] buf;
301   }
302   CmiFree(msg);
303 }
304
305
306
307
308 //Introspection object-- extract a list of CpdLists!
309 class CpdList_introspect : public CpdListAccessor {
310   CpdListTable_t *tab;
311 public:
312   CpdList_introspect(CpdListTable_t *tab_) :tab(tab_) { }
313   virtual const char *getPath(void) const { return "converse/lists";}
314   virtual size_t getLength(void) const {
315     size_t len=0;
316     CkHashtableIterator *it=tab->iterator();
317     while (NULL!=it->next()) len++;
318     delete it;
319     return len;
320   }
321   virtual void pup(PUP::er &p,CpdListItemsRequest &req) {
322     CkHashtableIterator *it=tab->iterator();
323     void *objp;
324     int curObj=0;
325     while (NULL!=(objp=it->next())) {
326       if (curObj>=req.lo && curObj<req.hi) {
327         CpdListAccessor *acc=*(CpdListAccessor **)objp;
328         char *pathName=(char *)acc->getPath();
329         beginItem(p,curObj);
330         p.comment("name");
331         p(pathName,strlen(pathName));
332       }
333       curObj++;
334     }
335   }
336 };
337
338
339
340
341 #endif /*CMK_CCS_AVAILABLE*/
342 /*We have to include these virtual functions, even when CCS is
343 disabled, to avoid bizarre link-time errors.*/
344
345 // C++ and C client API
346 void CpdListRegister(CpdListAccessor *acc)
347 #if CMK_CCS_AVAILABLE
348 {
349   CpvAccess(cpdListTable)->put(acc->getPath())=acc;
350 }
351 #else
352 { }
353 #endif
354
355 extern "C" void CpdListRegister_c(const char *path,
356             CpdListLengthFn_c len,void *lenParam,
357             CpdListItemsFn_c items,void *itemsParam,int checkBoundary)
358 #if CMK_CCS_AVAILABLE
359 {
360   CpdListRegister(new CpdListAccessor_c(path,
361              len,lenParam,items,itemsParam,checkBoundary!=0?true:false));
362 }
363 #else
364 { }
365 #endif
366
367 #if CMK_CCS_AVAILABLE
368
369
370 // Initialization       
371
372 static void CpdListInit(void) {
373   CpvInitialize(CpdListTable_t *,cpdListTable);
374   CpvAccess(cpdListTable)=new CpdListTable_t(31,0.5,
375               CkHashFunction_string,CkHashCompare_string);
376   CpdListRegister(new CpdList_introspect(CpvAccess(cpdListTable)));
377
378   CcsRegisterHandler("ccs_list_len",(CmiHandler)CpdList_ccs_list_len);
379   CcsRegisterHandler("ccs_list_items.txt",(CmiHandler)CpdList_ccs_list_items_txt);
380   CcsRegisterHandler("ccs_list_items.fmt",(CmiHandler)CpdList_ccs_list_items_fmt);
381   CcsRegisterHandler("ccs_list_items.set",(CmiHandler)CpdList_ccs_list_items_set);
382   CcsRegisterHandler("debug/converse/arch",(CmiHandler)CpdMachineArchitecture);
383 }
384
385 #if CMK_WEB_MODE
386 /******************************************************
387 Web performance monitoring interface:
388         Clients will register for performance data with 
389 processor 0 by calling the "perf_monitor" CCS request.  
390 Every WEB_INTERVAL (few seconds), this code
391 calls all registered web performance functions on all processors.  
392 The resulting integers are sent back to the client as a 
393 CCS reply.  
394
395 The current reply format is ASCII and rather nasty:
396 it's the string "perf" followed by space-separated list
397 of the performance functions for each processor.  By default,
398 only two performance functions are registered: the current 
399 processor utilization, in percent; and the current queue length.
400
401 The actual call sequence is:
402 CCS Client->CWebHandler->...  (processor 0)
403   ...->CWeb_Collect->... (all processors)
404 ...->CWeb_Reduce->CWeb_Deliver (processor 0 again)
405 */
406
407 #if 0
408 #  define WEBDEBUG(x) CmiPrintf x
409 #else
410 #  define WEBDEBUG(x) /*empty*/
411 #endif
412
413 #define WEB_INTERVAL 1000 /*Time, in milliseconds, between performance snapshots*/
414 #define MAXFNS 20 /*Largest number of performance functions to expect*/
415
416 typedef struct {
417         char hdr[CmiReservedHeaderSize];
418         int fromPE;/*Source processor*/
419         int perfData[MAXFNS];/*Performance numbers*/
420 } CWeb_CollectedData;
421
422 /*This needs to be made into a list of registered clients*/
423 static int hasApplet=0;
424 static CcsDelayedReply appletReply;
425
426 typedef int (*CWebFunction)(void);
427 static CWebFunction CWebPerformanceFunctionArray[MAXFNS];
428 static int CWebNoOfFns;
429 static int CWeb_ReduceIndex;
430 static int CWeb_CollectIndex;
431
432 /*Deliver the reduced web performance data to the waiting client:
433 */
434 static int collectedCount;
435 static CWeb_CollectedData **collectedValues;
436
437 static void CWeb_Deliver(void)
438 {
439   int i,j;
440
441   if (hasApplet) {
442     WEBDEBUG(("CWeb_Deliver to applet\n"));
443     /*Send the performance data off to the applet*/
444     char *reply=(char *)malloc(6+14*CmiNumPes()*CWebNoOfFns);
445     sprintf(reply,"perf");
446   
447     for(i=0; i<CmiNumPes(); i++){
448       for (j=0;j<CWebNoOfFns;j++)
449       {
450         char buf[20];
451         sprintf(buf," %d",collectedValues[i]->perfData[j]);
452         strcat(reply,buf);
453       }
454     }
455     CcsSendDelayedReply(appletReply,strlen(reply) + 1, reply);
456     free(reply);
457     hasApplet=0;
458   }
459   else
460     WEBDEBUG(("CWeb_Deliver (NO APPLET)\n"));
461   
462   /* Free saved performance data */
463   for(i = 0; i < CmiNumPes(); i++){
464     CmiFree(collectedValues[i]);
465     collectedValues[i] = 0;
466   }
467   collectedCount = 0;
468 }
469
470 /*On PE 0, this handler accumulates all the performace data
471 */
472 static void CWeb_Reduce(void *msg){
473   CWeb_CollectedData *cur,*prev;
474   int src;
475   if(CmiMyPe() != 0){
476     CmiAbort("CWeb performance data sent to wrong processor...\n");
477   }
478   WEBDEBUG(("CWeb_Reduce"));
479   cur=(CWeb_CollectedData *)msg;
480   src=cur->fromPE;
481   prev = collectedValues[src]; /* Previous value, ideally 0 */
482   collectedValues[src] = cur;
483   if(prev == 0) collectedCount++;
484   else CmiFree(prev); /*<- caused by out-of-order perf. data delivery*/
485
486   if(collectedCount == CmiNumPes()){
487     CWeb_Deliver();
488   }
489 }
490
491 /*On each PE, this handler collects the performance data
492 and sends it to PE 0.
493 */
494 static void CWeb_Collect(void)
495 {
496   CWeb_CollectedData *msg;
497   int i;
498
499   WEBDEBUG(("CWeb_Collect on %d\n",CmiMyPe()));
500   msg = (CWeb_CollectedData *)CmiAlloc(sizeof(CWeb_CollectedData));
501   msg->fromPE = CmiMyPe();
502   
503   /* Evaluate each performance function*/
504   for(i = 0; i < CWebNoOfFns; i++)
505     msg->perfData[i] = CWebPerformanceFunctionArray[i] ();
506
507   /* Send result off to node 0 */  
508   CmiSetHandler(msg, CWeb_ReduceIndex);
509   CmiSyncSendAndFree(0, sizeof(CWeb_CollectedData), msg);
510
511   /* Re-call this function after a delay */
512   CcdCallFnAfter((CcdVoidFn)CWeb_Collect, 0, WEB_INTERVAL);
513 }
514
515 extern "C" void CWebPerformanceRegisterFunction(CWebFunction fn)
516 {
517   if (CmiMyRank()!=0) return; /* Should only register from rank 0 */
518   if (CWebNoOfFns>=MAXFNS) CmiAbort("Registered too many CWebPerformance functions!");
519   CWebPerformanceFunctionArray[CWebNoOfFns] = fn;
520   CWebNoOfFns++;
521 }
522
523 /*This is called on PE 0 by clients that wish
524 to receive performance data.
525 */
526 static void CWebHandler(void){
527   if(CcsIsRemoteRequest()) {
528     static int startedCollection=0;
529     
530     WEBDEBUG(("CWebHandler request on %d\n",CmiMyPe()));    
531     hasApplet=1;
532     appletReply=CcsDelayReply();
533     
534     if(startedCollection == 0){
535       WEBDEBUG(("Starting data collection on every processor\n"));    
536       int i;
537       startedCollection=1;
538       collectedCount=0;
539       collectedValues = (CWeb_CollectedData **)malloc(sizeof(void *) * CmiNumPes());
540       for(i = 0; i < CmiNumPes(); i++)
541         collectedValues[i] = 0;
542       
543       /*Start collecting data on each processor*/
544       for(i = 0; i < CmiNumPes(); i++){
545         char *msg = (char *)CmiAlloc(CmiReservedHeaderSize);
546         CmiSetHandler(msg, CWeb_CollectIndex);
547         CmiSyncSendAndFree(i, CmiReservedHeaderSize,msg);
548       }
549     }
550   }
551 }
552
553 /** This "usage" section keeps track of percent of wall clock time
554 spent actually processing messages on each processor.   
555 It's a simple performance measure collected by the CWeb framework.
556 **/
557 struct CWebModeStats {
558 public:
559         double beginTime; ///< Start of last collection interval
560         double startTime; ///< Start of last busy time
561         double usedTime; ///< Total busy time in last collection interval
562         int PROCESSING; ///< If 1, processor is busy
563 };
564 CpvStaticDeclare(CWebModeStats *,cwebStats);
565
566 /* Called to begin a collection interval
567 */
568 static void usageReset(CWebModeStats *stats,double curWallTime)
569 {
570    stats->beginTime=curWallTime;
571    stats->usedTime = 0.;
572 }
573
574 /* Called when processor becomes busy
575 */
576 static void usageStart(CWebModeStats *stats,double curWallTime)
577 {
578    stats->startTime  = curWallTime;
579    stats->PROCESSING = 1;
580 }
581
582 /* Called when processor becomes idle
583 */
584 static void usageStop(CWebModeStats *stats,double curWallTime)
585 {
586    stats->usedTime   += curWallTime - stats->startTime;
587    stats->PROCESSING = 0;
588 }
589
590 /* Call this when the program is started
591  -> Whenever traceModuleInit would be called
592  -> -> see conv-core/convcore.c
593 */
594 static void initUsage()
595 {
596    CpvInitialize(CWebModeStats *, cwebStats);
597    CWebModeStats *stats=new CWebModeStats;
598    CpvAccess(cwebStats)=stats;
599    usageReset(stats,CmiWallTimer());
600    usageStart(stats,CmiWallTimer());
601    CcdCallOnConditionKeep(CcdPROCESSOR_BEGIN_BUSY,(CcdVoidFn)usageStart,stats);
602    CcdCallOnConditionKeep(CcdPROCESSOR_BEGIN_IDLE,(CcdVoidFn)usageStop,stats);    
603 }
604
605 static int getUsage(void)
606 {
607    int usage = 0;
608    double time      = CmiWallTimer();
609    CWebModeStats *stats=CpvAccess(cwebStats);
610    double totalTime = time - stats->beginTime;
611
612    if(stats->PROCESSING)
613    { /* Lock in current CPU usage */
614       usageStop(stats,time); usageStart(stats,time);
615    }
616    if(totalTime > 0.)
617       usage = (int)(0.5 + 100 *stats->usedTime/totalTime);
618    usageReset(stats,time);
619
620    return usage;
621 }
622
623 static int getSchedQlen(void)
624 {
625   return(CqsLength((Queue)CpvAccess(CsdSchedQueue)));
626 }
627
628 #endif /*CMK_WEB_MODE*/
629
630 #if ! CMK_WEB_MODE
631 static void CWeb_Invalid(void)
632 {
633   CmiAbort("Invalid web mode handler invoked!\n");
634 }
635 #endif
636
637 void CWebInit(void)
638 {
639 #if CMK_WEB_MODE
640   CcsRegisterHandler("perf_monitor", (CmiHandler)CWebHandler);
641   
642   CWeb_CollectIndex=CmiRegisterHandler((CmiHandler)CWeb_Collect);
643   CWeb_ReduceIndex=CmiRegisterHandler((CmiHandler)CWeb_Reduce);
644   
645   initUsage();
646   CWebPerformanceRegisterFunction(getUsage);
647   CWebPerformanceRegisterFunction(getSchedQlen);
648 #else
649   /* always maintain the consistent CmiHandler table */
650   /* which is good for heterogeneous clusters */
651   CmiRegisterHandler((CmiHandler)CWeb_Invalid);
652   CmiRegisterHandler((CmiHandler)CWeb_Invalid);
653 #endif
654 }
655
656
657 extern "C" void CcsBuiltinsInit(char **argv)
658 {
659   CcsRegisterHandler("ccs_getinfo",(CmiHandler)ccs_getinfo);
660   CcsRegisterHandler("ccs_killport",(CmiHandler)ccs_killport);
661   CcsRegisterHandler("ccs_killpe",(CmiHandler)ccs_killpe);
662   CWebInit();
663   CpdListInit();
664 }
665
666
667 #endif /*CMK_CCS_AVAILABLE*/
668
669 void PUP_fmt::fieldHeader(typeCode_t typeCode,int nItems) {
670     // Compute and write intro byte:
671     lengthLen_t ll;
672     if (nItems==1) ll=lengthLen_single;
673     else if (nItems<256) ll=lengthLen_byte;
674     else ll=lengthLen_int;
675     // CmiPrintf("Intro byte: l=%d t=%d\n",(int)ll,(int)typeCode);
676     byte intro=(((int)ll)<<4)+(int)typeCode;
677     p(intro);
678     // Compute and write length:
679     switch(ll) {
680     case lengthLen_single: break; // Single item
681     case lengthLen_byte: {
682         byte l=nItems;
683         p(l);
684         } break;
685     case lengthLen_int: {
686         p(nItems); 
687         } break; 
688     };
689 }
690
691 void PUP_fmt::comment(const char *message) {
692         int nItems=strlen(message);
693         fieldHeader(typeCode_comment,nItems);
694         p((char *)message,nItems);
695 }
696 void PUP_fmt::synchronize(unsigned int m) {
697         fieldHeader(typeCode_sync,1);
698         p(m);
699 }
700 void PUP_fmt::bytes(void *ptr,int n,size_t itemSize,PUP::dataType t) {
701         switch(t) {
702         case PUP::Tchar:
703         case PUP::Tuchar:
704         case PUP::Tbyte:
705                 fieldHeader(typeCode_byte,n);
706                 p.bytes(ptr,n,itemSize,t);
707                 break;
708         case PUP::Tshort: case PUP::Tint:
709         case PUP::Tushort: case PUP::Tuint:
710         case PUP::Tbool:
711                 fieldHeader(typeCode_int,n);
712                 p.bytes(ptr,n,itemSize,t);
713                 break;
714         // treat "long" and "pointer" as 8-bytes, in conformity with pup_toNetwork.C
715         case PUP::Tlong: case PUP::Tlonglong:
716         case PUP::Tulong: case PUP::Tulonglong:
717                 fieldHeader(typeCode_long,n);
718                 p.bytes(ptr,n,itemSize,t);
719                 break;
720         case PUP::Tfloat:
721                 fieldHeader(typeCode_float,n);
722                 p.bytes(ptr,n,itemSize,t);
723                 break;
724         case PUP::Tdouble: case PUP::Tlongdouble:
725                 fieldHeader(typeCode_double,n);
726                 p.bytes(ptr,n,itemSize,t);
727                 break;
728     case PUP::Tpointer:
729         fieldHeader(typeCode_pointer,n);
730         p.bytes(ptr,n,itemSize,t);
731         break;
732         default: CmiAbort("Unrecognized type code in PUP_fmt::bytes");
733         };
734 }
735
736