4caf68214555ce2f7044af3c954b5d63ca1cf078
[charm.git] / src / ck-core / debug-charm.C
1 /*****************************************************************************
2  * A few useful built-in CPD and CCS handlers.
3  *****************************************************************************/
4
5 #include "converse.h"
6
7 #include <errno.h>
8 #include <string.h>
9 #include <sys/stat.h>           // for chmod
10
11 #include "ckhashtable.h"
12 #include "conv-ccs.h"
13 #include "debug-charm.h"
14 #include "sockRoutines.h"
15 #include "charm.h"
16 #include "middle.h"
17 #include "cklists.h"
18 #include "register.h"
19 //#include "queueing.h"
20
21 #if CMK_CCS_AVAILABLE
22
23 #include "ck.h"
24
25
26 /************ Array Element CPD Lists ****************/
27
28 /**
29   Count array elements going by until they reach this 
30   range (lo to hi), then start passing them to dest.
31 */
32 template <class T>
33 class CkArrayElementRangeIterator : public CkLocIterator {
34 private:
35    T *dest;
36    CkArray *mgr;
37    int cur,lo,hi;
38 public:
39    CkArrayElementRangeIterator(T *dest_,int l,int h) 
40         :dest(dest_),mgr(0),cur(0),lo(l),hi(h) {}
41    
42   /** Called to iterate only on a specific array manager.
43       Returs the number of objects it iterate on.
44   */
45   int iterate(int start, CkArray *m) {
46     cur = start;
47     mgr = m;
48     mgr->getLocMgr()->iterate(*this);
49     cur -= start;
50     return cur;
51   }
52
53    /** Call add for every in-range array element on this processor */
54    void iterate(void)
55    { /* Walk the groupTable for arrays (FIXME: get rid of _groupIDTable) */
56      int numGroups=CkpvAccess(_groupIDTable)->size();
57      for(int i=0;i<numGroups;i++) {
58         IrrGroup *obj = CkpvAccess(_groupTable)->find((*CkpvAccess(_groupIDTable))[i]).getObj();
59         if (obj->isArrMgr()) 
60         { /* This is an array manager: examine its array elements */
61           mgr=(CkArray *)obj;
62           mgr->getLocMgr()->iterate(*this);
63         }
64      }
65    }
66    
67    // Called by location manager's iterate function
68    virtual void addLocation (CkLocation &loc)
69    {
70      if (cur>=lo && cur<hi) 
71      { /* This element is in our range-- look it up */
72        dest->add(cur,mgr->lookup(loc.getIndex()));
73      }
74      cur++;
75    }
76    
77    // Return the number of total array elements seen so far.
78    int getCount(void) {return cur;}
79 };
80
81 /**
82   Count charm++ objects going by until they reach this 
83   range (lo to hi), then start passing them to dest.
84 */
85 template <class T>
86 class CkObjectRangeIterator {
87 private:
88    T *dest;
89    int cur,lo,hi;
90 public:
91    CkObjectRangeIterator(T *dest_,int l,int h) 
92         :dest(dest_),cur(0),lo(l),hi(h) {}
93    
94    /** Call add for every in-range array element on this processor */
95    void iterate(void)
96    { /* Walk the groupTable for arrays (FIXME: get rid of _groupIDTable) */
97      int numGroups=CkpvAccess(_groupIDTable)->size();
98      for(int i=0;i<numGroups;i++) {
99         IrrGroup *obj = CkpvAccess(_groupTable)->find((*CkpvAccess(_groupIDTable))[i]).getObj();
100         if (obj->isArrMgr()) 
101         { /* This is an array manager: examine its array elements */
102           CkArray *mgr=(CkArray *)obj;
103           CkArrayElementRangeIterator<T> ait(dest,lo,hi);
104           ait.iterate(cur, mgr);
105           cur+=ait.getCount();
106         } else {
107           dest->add(cur,obj);
108           cur++;
109         }
110      }
111    }
112    
113    // Return the number of total array elements seen so far.
114    int getCount(void) {return cur;}
115 };
116
117 class ignoreAdd {
118 public: void add(int cur,Chare *elt) {}
119 };
120
121 /** Examine all the objects on the server returning the name */
122 class CpdList_objectNames : public CpdListAccessor {
123   PUP::er *pp; // Only used while inside pup routine.
124   int curGroup;
125 public:
126   virtual const char * getPath(void) const {return "charm/objectNames";}
127   virtual int getLength(void) const {
128     CkObjectRangeIterator<ignoreAdd> it(0,0,0);
129     it.iterate();
130     return it.getCount();
131   }
132   virtual void pup(PUP::er &p, CpdListItemsRequest &req) {
133     pp=&p;
134     CkObjectRangeIterator<CpdList_objectNames> it(this,req.lo,req.hi);
135     it.iterate(); // calls "add" for in-range elements
136   }
137   void add(int cur, Chare *obj) {
138     PUP::er &p=*pp;
139     beginItem(p,cur);
140     p.comment("id");
141     char *n = (char*)malloc(30);
142     int s=obj->ckDebugChareID(n, 30);
143     CkAssert(s > 0);
144     p(n,s);
145     free(n);
146     p.comment("name");
147     n=obj->ckDebugChareName();
148     p(n,strlen(n));
149     free(n);
150   }
151 };
152
153 /** Examine a single object identified by the id passed in the request and
154     return its type and memory data */
155 class CpdList_object : public CpdListAccessor {
156   PUP::er *pp; //Only used while inside pup routine.
157   CpdListItemsRequest *reqq; // Only used while inside pup routine.
158 public:
159   virtual const char * getPath(void) const {return "charm/object";}
160   virtual int getLength(void) const {
161     CkObjectRangeIterator<ignoreAdd> it(0,0,0);
162     it.iterate();
163     return it.getCount();
164   }
165   virtual void pup(PUP::er &p, CpdListItemsRequest &req) {
166     pp=&p;
167     reqq=&req;
168     CkObjectRangeIterator<CpdList_object> it(this,req.lo,req.hi);
169     it.iterate(); // calls "add" for in-range elements;
170   }
171   void add(int cur, Chare *obj) {
172     PUP::er &p=*pp;
173     CpdListItemsRequest &req=*reqq;
174     char *n = (char *)malloc(30);
175     int s=obj->ckDebugChareID(n, 30);
176     CkAssert(s > 0);
177     if (req.extraLen == s && memcmp(req.extra, n, s) == 0) {
178       // the object match, found!
179       beginItem(p,cur);
180       int type = obj->ckGetChareType();
181       p.comment("type");
182       const char *t = _chareTable[type]->name;
183       p((char*)t,strlen(t));
184       p.comment("value");
185       int size = _chareTable[type]->size;
186       p((char*)obj,size);
187     }
188   }
189 };
190
191 /** Coarse: examine array element names */
192 class CpdList_arrayElementNames : public CpdListAccessor {
193   PUP::er *pp; // Only used while inside pup routine.
194 public:
195   virtual const char * getPath(void) const {return "charm/arrayElementNames";}
196   virtual int getLength(void) const {
197     CkArrayElementRangeIterator<ignoreAdd> it(0,0,0);
198     it.iterate();
199     return it.getCount();
200   }
201   virtual void pup(PUP::er &p, CpdListItemsRequest &req) {
202     pp=&p;
203     CkArrayElementRangeIterator<CpdList_arrayElementNames> it(this,req.lo,req.hi);
204     it.iterate(); // calls "add" for in-range elements
205   }
206   void add(int cur,Chare *e) 
207   { // Just grab the name and nothing else:
208     ArrayElement *elt = (ArrayElement*)e;
209          PUP::er &p=*pp;
210          beginItem(p,cur);
211          p.comment("name");
212          char *n=elt->ckDebugChareName();
213          p(n,strlen(n));
214          free(n);
215   }
216 };
217
218 /** Detailed: examine array element data */
219 class CpdList_arrayElements : public CpdListAccessor {
220   PUP::er *pp; // Only used while inside pup routine.
221 public:
222   virtual const char * getPath(void) const {return "charm/arrayElements";}
223   virtual int getLength(void) const {
224     CkArrayElementRangeIterator<ignoreAdd> it(0,0,0);
225     it.iterate();
226     return it.getCount();
227   }
228   virtual void pup(PUP::er &p, CpdListItemsRequest &req) {
229     pp=&p;
230     CkArrayElementRangeIterator<CpdList_arrayElements> it(this,req.lo,req.hi);
231     it.iterate(); // calls "add" for in-range elements
232   }
233   void add(int cur, Chare *e) 
234   { // Pup the element data
235     ArrayElement *elt = (ArrayElement*)e;
236     PUP::er &p=*pp;
237     beginItem(p,cur);
238     //elt->ckDebugPup(p);
239     // Now ignore any pupper, just copy all the memory as raw data
240     p.comment("name");
241     char *n=elt->ckDebugChareName();
242     p(n,strlen(n));
243     free(n);
244     int type = elt->ckGetChareType();
245     p.comment("type");
246     const char *t = _chareTable[type]->name;
247     p((char*)t,strlen(t));
248     p.comment("value");
249     int size = _chareTable[type]->size;
250     p((char*)elt,size);
251   }
252 };
253
254 #include <rpc/rpc.h>
255
256 int hostInfoLength(void *) {return 1;}
257
258 void hostInfo(void *itemIter, pup_er pp, CpdListItemsRequest *req) {
259   PUP::er &p = *(PUP::er *)pp;
260   struct sockaddr_in addr;
261   CpdListBeginItem(pp, 0);
262 #if CMK_HAS_GET_MYADDRESS
263   get_myaddress(&addr);
264 #else
265   CmiAbort("hostInfo: get_myaddress does not work on this machine");
266 #endif
267   char *address = (char*)&addr.sin_addr.s_addr;
268   PUPv(address, 4);
269   int pid = getpid();
270   PUPn(pid);
271 }
272
273 /************ Message CPD Lists ****************/
274 CpvCExtern(void *,debugQueue);
275
276 // Interpret data in a message in a user-friendly way.
277 //  Ignores most of the envelope fields used by CkPupMessage,
278 //  and instead concentrates on user data
279 void CpdPupMessage(PUP::er &p, void *msg)
280 {
281   envelope *env=UsrToEnv(msg);
282   //int wasPacked=env->isPacked();
283   int size=env->getTotalsize();
284   int prioBits=env->getPriobits();
285   int from=env->getSrcPe();
286   PUPn(from);
287   //PUPn(wasPacked);
288   PUPn(prioBits);
289   int userSize=size-sizeof(envelope)-sizeof(int)*CkPriobitsToInts(prioBits);
290   PUPn(userSize);
291   int msgType = env->getMsgIdx();
292   PUPn(msgType);
293   int msgFor = env->getMsgtype();
294   PUPn(msgFor);
295
296   //p.synchronize(PUP::sync_last_system);
297   
298   int ep=CkMessageToEpIdx(msg);
299   PUPn(ep);
300   
301   /* user data */
302   p.comment("data");
303   p.synchronize(PUP::sync_begin_object);
304   if (_entryTable[ep]->messagePup!=NULL) 
305     _entryTable[ep]->messagePup(p,msg);
306   else
307     CkMessage::ckDebugPup(p,msg);
308   p.synchronize(PUP::sync_end_object);
309 }
310
311 //Cpd Lists for local and scheduler queues
312 class CpdList_localQ : public CpdListAccessor {
313
314 public:
315   CpdList_localQ() {}
316   virtual const char * getPath(void) const {return "converse/localqueue";}
317   virtual int getLength(void) const {
318     int x = CdsFifo_Length((CdsFifo)(CpvAccess(debugQueue)));
319     //CmiPrintf("*******Returning fifo length %d*********\n", x);
320     //return CdsFifo_Length((CdsFifo)(CpvAccess(CmiLocalQueue)));
321     return x;
322   }
323   virtual void pup(PUP::er &p, CpdListItemsRequest &req) {
324     int length = CdsFifo_Length((CdsFifo)(CpvAccess(debugQueue)));
325     void ** messages = CdsFifo_Enumerate(CpvAccess(debugQueue));
326     int curObj=0;
327     
328     for(curObj=req.lo; curObj<req.hi; curObj++)
329     if ((curObj>=0) && (curObj<length))
330     {
331         beginItem(p,curObj);
332         void *msg=messages[curObj]; /* converse message */
333         int isCharm=0;
334         const char *type="Converse";
335         p.comment("name");
336         char name[128];
337         if (CmiGetHandler(msg)==_charmHandlerIdx) {isCharm=1; type="Local Charm";}
338         if (CmiGetXHandler(msg)==_charmHandlerIdx) {isCharm=1; type="Network Charm";}
339         sprintf(name,"%s %d: %s (%d)","Message",curObj,type,CmiGetHandler(msg));
340         p(name, strlen(name));
341         
342         if (isCharm) 
343         { /* charm message */
344           p.comment("charmMsg");
345           p.synchronize(PUP::sync_begin_object);
346           envelope *env=(envelope *)msg;
347           CkUnpackMessage(&env);
348           messages[curObj]=env;
349           CpdPupMessage(p, EnvToUsr(env));
350           //CkPupMessage(p, &messages[curObj], 0);
351           p.synchronize(PUP::sync_end_object);
352         }
353     }
354     delete[] messages;
355
356   }
357 };
358
359
360 /****************** Breakpoints and other debug support **************/
361
362 typedef CkHashtableTslow<int,EntryInfo *> CpdBpFuncTable_t;
363
364
365 extern void CpdFreeze(void);
366 extern void CpdUnFreeze(void);
367
368
369 CpvStaticDeclare(int, _debugMsg);
370 CpvStaticDeclare(int, _debugChare);
371
372 CpvStaticDeclare(CpdBpFuncTable_t *, breakPointEntryTable);
373
374 CpvStaticDeclare(void *, lastBreakPointMsg);
375 CpvStaticDeclare(void *, lastBreakPointObject);
376 CpvStaticDeclare(int, lastBreakPointIndex);
377
378 void CpdBreakPointInit()
379 {
380   CpvInitialize(void *, lastBreakPointMsg);
381   CpvInitialize(void *, lastBreakPointObject);
382   CpvInitialize(int, lastBreakPointIndex);
383   CpvInitialize(int, _debugMsg);
384   CpvInitialize(int, _debugChare);
385   CpvInitialize(CpdBpFuncTable_t *, breakPointEntryTable);
386   CpvAccess(lastBreakPointMsg) = NULL;
387   CpvAccess(lastBreakPointObject) = NULL;
388   CpvAccess(lastBreakPointIndex) = 0;
389   CpvAccess(_debugMsg) = CkRegisterMsg("debug_msg",0,0,0);
390   CpvAccess(_debugChare) = CkRegisterChare("debug_Chare",0);
391   CpvAccess(breakPointEntryTable) = new CpdBpFuncTable_t(10,0.5,CkHashFunction_int,CkHashCompare_int );
392 }
393
394
395
396 static void _call_freeze_on_break_point(void * msg, void * object)
397 {
398       //Save breakpoint entry point index. This is retrieved from msg.
399       //So that the appropriate EntryInfo can be later retrieved from the hash table 
400       //of break point function entries, on continue.
401       CpvAccess(lastBreakPointMsg) = msg;
402       CpvAccess(lastBreakPointObject) = object;
403       CpvAccess(lastBreakPointIndex) = CkMessageToEpIdx(msg);
404       EntryInfo * breakPointEntryInfo = CpvAccess(breakPointEntryTable)->get(CpvAccess(lastBreakPointIndex));
405       CmiPrintf("Break point reached for Function = %s\n", breakPointEntryInfo->name);
406       CpdFreeze();
407 }
408
409
410
411 //ccs handler when continue from a break point
412 extern "C"
413 void CpdContinueFromBreakPoint ()
414 {
415     CpdUnFreeze();
416     if ( (CpvAccess(lastBreakPointMsg) != NULL) && (CpvAccess(lastBreakPointObject) != NULL) )
417     {
418         EntryInfo * breakPointEntryInfo = CpvAccess(breakPointEntryTable)->get(CpvAccess(lastBreakPointIndex));
419         if (breakPointEntryInfo != NULL)
420            breakPointEntryInfo->call(CpvAccess(lastBreakPointMsg), CpvAccess(lastBreakPointObject));
421     }
422     CpvAccess(lastBreakPointMsg) = NULL;
423     CpvAccess(lastBreakPointObject) = NULL;
424 }
425
426 //ccs handler to set a breakpoint with entry function name msg
427 void CpdSetBreakPoint (char *msg)
428 {
429   char functionName[128];
430   int tableSize, tableIdx = 0;
431   sscanf(msg+CmiMsgHeaderSizeBytes, "%s", functionName);
432   if (strlen(functionName) > 0)
433   {
434     tableSize = _entryTable.size();
435     // Replace entry in entry table with _call_freeze_on_break_point
436     // retrieve epIdx for entry method
437     for (tableIdx=0; tableIdx < tableSize; tableIdx++)
438     {
439        if (strstr(_entryTable[tableIdx]->name, functionName) != NULL)
440        {
441             EntryInfo * breakPointEntryInfo = new EntryInfo(_entryTable[tableIdx]->name, _entryTable[tableIdx]->call, _entryTable[tableIdx]->msgIdx, _entryTable[tableIdx]->chareIdx );
442            CmiPrintf("Breakpoint is set for function %s with an epIdx = %ld\n", _entryTable[tableIdx]->name, tableIdx);
443            CpvAccess(breakPointEntryTable)->put(tableIdx) = breakPointEntryInfo;  
444            _entryTable[tableIdx]->name = "debug_breakpoint_ep";  
445            _entryTable[tableIdx]->call = (CkCallFnPtr)_call_freeze_on_break_point;
446            _entryTable[tableIdx]->msgIdx = CpvAccess(_debugMsg); 
447            _entryTable[tableIdx]->chareIdx = CpvAccess(_debugChare);
448            break;
449        }
450     }
451     if (tableIdx == tableSize)
452     {
453       CmiPrintf("[ERROR]Entrypoint was not found for function %s\n", functionName); 
454       return;
455     }
456
457   }
458
459 }
460
461 void CpdQuitDebug()
462 {
463   CpdContinueFromBreakPoint();
464   CkExit();
465 }
466
467 void CpdRemoveBreakPoint (char *msg)
468 {
469   char functionName[128];
470   sscanf(msg+CmiMsgHeaderSizeBytes, "%s", functionName);
471   void *objPointer;
472   void *keyPointer; 
473   CkHashtableIterator *it = CpvAccess(breakPointEntryTable)->iterator();
474   while(NULL!=(objPointer = it->next(&keyPointer)))
475   {
476     EntryInfo * breakPointEntryInfo = *(EntryInfo **)objPointer;
477     int idx = *(int *)keyPointer;
478     if (strstr(breakPointEntryInfo->name, functionName) != NULL){
479         _entryTable[idx]->name =  breakPointEntryInfo->name;
480         _entryTable[idx]->call = (CkCallFnPtr)breakPointEntryInfo->call;
481         _entryTable[idx]->msgIdx = breakPointEntryInfo->msgIdx;
482         _entryTable[idx]->chareIdx = breakPointEntryInfo->chareIdx;
483         CmiPrintf("Breakpoint is removed for function %s with epIdx %ld\n", _entryTable[idx]->name, idx);
484     }
485   }
486 }
487
488 void CpdRemoveAllBreakPoints ()
489 {
490   //all breakpoints removed
491   void *objPointer;
492   void *keyPointer; 
493   CkHashtableIterator *it = CpvAccess(breakPointEntryTable)->iterator();
494   while(NULL!=(objPointer = it->next(&keyPointer)))
495   {
496     EntryInfo * breakPointEntryInfo = *(EntryInfo **)objPointer;
497     int idx = *(int *)keyPointer;
498     _entryTable[idx]->name =  breakPointEntryInfo->name;
499     _entryTable[idx]->call = (CkCallFnPtr)breakPointEntryInfo->call;
500     _entryTable[idx]->msgIdx = breakPointEntryInfo->msgIdx;
501     _entryTable[idx]->chareIdx = breakPointEntryInfo->chareIdx;
502   }
503 }
504
505
506
507
508
509 CpvExtern(char *, displayArgument);
510
511 void CpdStartGdb(void)
512 {
513 #if !defined(_WIN32) || defined(__CYGWIN__)
514   FILE *f;
515   char gdbScript[200];
516   int pid;
517   if (CpvAccess(displayArgument) != NULL)
518   {
519      /*CmiPrintf("MY NODE IS %d  and process id is %d\n", CmiMyPe(), getpid());*/
520      sprintf(gdbScript, "/tmp/cpdstartgdb.%d.%d", getpid(), CmiMyPe());
521      f = fopen(gdbScript, "w");
522      fprintf(f,"#!/bin/sh\n");
523      fprintf(f,"cat > /tmp/start_gdb.$$ << END_OF_SCRIPT\n");
524      fprintf(f,"shell /bin/rm -f /tmp/start_gdb.$$\n");
525      //fprintf(f,"handle SIGPIPE nostop noprint\n");
526      fprintf(f,"handle SIGWINCH nostop noprint\n");
527      fprintf(f,"handle SIGWAITING nostop noprint\n");
528      fprintf(f, "attach %d\n", getpid());
529      fprintf(f,"END_OF_SCRIPT\n");
530      fprintf(f, "DISPLAY='%s';export DISPLAY\n",CpvAccess(displayArgument));
531      fprintf(f,"/usr/X11R6/bin/xterm ");
532      fprintf(f," -title 'Node %d ' ",CmiMyPe());
533      fprintf(f," -e /usr/bin/gdb -x /tmp/start_gdb.$$ \n");
534      fprintf(f, "exit 0\n");
535      fclose(f);
536      if( -1 == chmod(gdbScript, 0755))
537      {
538         CmiPrintf("ERROR> chmod on script failed!\n");
539         return;
540      }
541      pid = fork();
542      if (pid < 0)
543         { perror("ERROR> forking to run debugger script\n"); exit(1); }
544      if (pid == 0)
545      {
546          //CmiPrintf("In child process to start script %s\n", gdbScript);
547  if (-1 == execvp(gdbScript, NULL))
548             CmiPrintf ("Error> Could not Execute Debugger Script: %s\n",strerror
549 (errno));
550
551       }
552     }
553 #endif
554 }
555
556 void CpdCharmInit()
557 {
558   CpdBreakPointInit();
559   CcsRegisterHandler("ccs_set_break_point",(CmiHandler)CpdSetBreakPoint);
560   CcsRegisterHandler("ccs_remove_break_point",(CmiHandler)CpdRemoveBreakPoint);
561   CcsRegisterHandler("ccs_remove_all_break_points",(CmiHandler)CpdRemoveAllBreakPoints);
562   CcsRegisterHandler("ccs_continue_break_point",(CmiHandler)CpdContinueFromBreakPoint);
563   CcsRegisterHandler("ccs_debug_quit",(CmiHandler)CpdQuitDebug);
564   CcsRegisterHandler("ccs_debug_startgdb",(CmiHandler)CpdStartGdb);
565   CpdListRegister(new CpdListAccessor_c("hostinfo",hostInfoLength,0,hostInfo,0));
566   CpdListRegister(new CpdList_localQ());
567   CpdListRegister(new CpdList_arrayElementNames());
568   CpdListRegister(new CpdList_arrayElements());
569   CpdListRegister(new CpdList_objectNames());
570   CpdListRegister(new CpdList_object());
571 }
572
573
574 #endif /*CMK_CCS_AVAILABLE*/