xref: /petsc/src/sys/error/adebug.c (revision a8158fb5a3692ffc34efd85261445929d2a1359c)
1 /*
2       Code to handle PETSc starting up in debuggers,etc.
3 */
4 
5 #include <petscsys.h>               /*I   "petscsys.h"   I*/
6 #include <signal.h>
7 #if defined(PETSC_HAVE_UNISTD_H)
8 #include <unistd.h>
9 #endif
10 
11 /*
12       These are the debugger and display used if the debugger is started up
13 */
14 static char      PetscDebugger[PETSC_MAX_PATH_LEN];
15 static char      DebugTerminal[PETSC_MAX_PATH_LEN];
16 static PetscBool Xterm = PETSC_TRUE;
17 
18 /*@C
19    PetscSetDebugTerminal - Sets the terminal to use (instead of xterm) for debugging.
20 
21    Not Collective
22 
23    Input Parameters:
24 .  terminal - name of terminal and any flags required to execute a program.
25               For example "xterm -e", "urxvt -e", "gnome-terminal -x".
26 
27    Options Database Keys:
28    -debug_terminal terminal - use this terminal instead of xterm
29 
30    Level: developer
31 
32    Notes:
33    You can start the debugger for all processes in the same GNU screen session.
34 
35      mpiexec -n 4 ./myapp -start_in_debugger -debug_terminal "screen -X -S debug screen"
36 
37    will open 4 windows in the session named "debug".
38 
39    Fortran Note:
40    This routine is not supported in Fortran.
41 
42 .seealso: PetscSetDebugger()
43 @*/
44 PetscErrorCode  PetscSetDebugTerminal(const char terminal[])
45 {
46   PetscErrorCode ierr;
47 
48   PetscFunctionBegin;
49   ierr = PetscStrcpy(DebugTerminal,terminal);CHKERRQ(ierr);
50   PetscFunctionReturn(0);
51 }
52 
53 /*@C
54    PetscSetDebugger - Sets options associated with the debugger.
55 
56    Not Collective
57 
58    Input Parameters:
59 +  debugger - name of debugger, which should be in your path,
60               usually "lldb", "dbx", "gdb", "idb", "xxgdb", "kdgb" or "ddd". Also, HP-UX
61               supports "xdb", and IBM rs6000 supports "xldb".
62 
63 -  xterm - flag to indicate debugger window, set to either PETSC_TRUE (to indicate
64             debugger should be started in a new xterm) or PETSC_FALSE (to start debugger
65             in initial window (the option PETSC_FALSE makes no sense when using more
66             than one MPI process.)
67 
68    Level: developer
69 
70    Fortran Note:
71    This routine is not supported in Fortran.
72 
73 .seealso: PetscAttachDebugger(), PetscAttachDebuggerErrorHandler()
74 @*/
75 PetscErrorCode  PetscSetDebugger(const char debugger[],PetscBool xterm)
76 {
77   PetscErrorCode ierr;
78 
79   PetscFunctionBegin;
80   if (debugger) {
81     ierr = PetscStrcpy(PetscDebugger,debugger);CHKERRQ(ierr);
82   }
83   Xterm = xterm;
84   PetscFunctionReturn(0);
85 }
86 
87 /*@C
88     PetscSetDefaultDebugger - Causes PETSc to use its default  debugger.
89 
90    Not collective
91 
92     Level: developer
93 
94 .seealso: PetscSetDebugger(), PetscSetDebuggerFromString()
95 @*/
96 PetscErrorCode  PetscSetDefaultDebugger(void)
97 {
98   PetscErrorCode ierr;
99 
100   PetscFunctionBegin;
101 #if defined(PETSC_USE_LLDB_DEBUGGER)
102   ierr = PetscSetDebugger("lldb",PETSC_TRUE);CHKERRQ(ierr);
103 #elif defined(PETSC_USE_DBX_DEBUGGER)
104   ierr = PetscSetDebugger("dbx",PETSC_TRUE);CHKERRQ(ierr);
105 #elif defined(PETSC_USE_XDB_DEBUGGER)
106   ierr = PetscSetDebugger("xdb",PETSC_TRUE);CHKERRQ(ierr);
107 #elif defined(PETSC_USE_IDB_DEBUGGER)
108   ierr = PetscSetDebugger("idb",PETSC_TRUE);CHKERRQ(ierr);
109 #else  /* Default is gdb */
110   ierr = PetscSetDebugger("gdb",PETSC_TRUE);CHKERRQ(ierr);
111 #endif
112   ierr = PetscSetDebugTerminal("xterm -e");CHKERRQ(ierr);
113   PetscFunctionReturn(0);
114 }
115 
116 static PetscErrorCode PetscCheckDebugger_Private(const char defaultDbg[], const char string[], const char *debugger[])
117 {
118   PetscBool      exists;
119   char           *f;
120   PetscErrorCode ierr;
121 
122   PetscFunctionBegin;
123   ierr = PetscStrstr(string, defaultDbg, &f);CHKERRQ(ierr);
124   if (f) {
125     ierr = PetscTestFile(string, 'x', &exists);CHKERRQ(ierr);
126     if (exists) *debugger = string;
127     else        *debugger = defaultDbg;
128   }
129   PetscFunctionReturn(0);
130 }
131 
132 /*@C
133     PetscSetDebuggerFromString - Set the complete path for the
134        debugger for PETSc to use.
135 
136    Not collective
137 
138    Level: developer
139 
140 .seealso: PetscSetDebugger(), PetscSetDefaultDebugger()
141 @*/
142 PetscErrorCode  PetscSetDebuggerFromString(const char *string)
143 {
144   const char     *debugger = NULL;
145   PetscBool      xterm     = PETSC_TRUE;
146   char           *f;
147   PetscErrorCode ierr;
148 
149   PetscFunctionBegin;
150   ierr = PetscStrstr(string, "noxterm", &f);CHKERRQ(ierr);
151   if (f) xterm = PETSC_FALSE;
152   ierr = PetscStrstr(string, "ddd", &f);CHKERRQ(ierr);
153   if (f) xterm = PETSC_FALSE;
154   ierr = PetscCheckDebugger_Private("xdb",      string, &debugger);CHKERRQ(ierr);
155   ierr = PetscCheckDebugger_Private("dbx",      string, &debugger);CHKERRQ(ierr);
156   ierr = PetscCheckDebugger_Private("xldb",     string, &debugger);CHKERRQ(ierr);
157   ierr = PetscCheckDebugger_Private("gdb",      string, &debugger);CHKERRQ(ierr);
158   ierr = PetscCheckDebugger_Private("idb",      string, &debugger);CHKERRQ(ierr);
159   ierr = PetscCheckDebugger_Private("xxgdb",    string, &debugger);CHKERRQ(ierr);
160   ierr = PetscCheckDebugger_Private("ddd",      string, &debugger);CHKERRQ(ierr);
161   ierr = PetscCheckDebugger_Private("kdbg",     string, &debugger);CHKERRQ(ierr);
162   ierr = PetscCheckDebugger_Private("ups",      string, &debugger);CHKERRQ(ierr);
163   ierr = PetscCheckDebugger_Private("workshop", string, &debugger);CHKERRQ(ierr);
164   ierr = PetscCheckDebugger_Private("pgdbg",    string, &debugger);CHKERRQ(ierr);
165   ierr = PetscCheckDebugger_Private("pathdb",   string, &debugger);CHKERRQ(ierr);
166   ierr = PetscCheckDebugger_Private("lldb",     string, &debugger);CHKERRQ(ierr);
167 
168   ierr = PetscSetDebugger(debugger, xterm);CHKERRQ(ierr);
169   PetscFunctionReturn(0);
170 }
171 
172 
173 /*@
174    PetscAttachDebugger - Attaches the debugger to the running process.
175 
176    Not Collective
177 
178    Level: advanced
179 
180    Developer Notes:
181     Since this can be called by the error handler should it be calling SETERRQ() and CHKERRQ()?
182 
183 .seealso: PetscSetDebugger()
184 @*/
185 PetscErrorCode  PetscAttachDebugger(void)
186 {
187 #if !defined(PETSC_CANNOT_START_DEBUGGER)
188   int            child    =0;
189   PetscReal      sleeptime=0;
190   PetscErrorCode ierr;
191   char           program[PETSC_MAX_PATH_LEN],display[256],hostname[64];
192 #endif
193 
194   PetscFunctionBegin;
195 #if defined(PETSC_CANNOT_START_DEBUGGER) || !defined(PETSC_HAVE_FORK)
196   (*PetscErrorPrintf)("System cannot start debugger\n");
197   (*PetscErrorPrintf)("On Cray run program in Totalview debugger\n");
198   (*PetscErrorPrintf)("On Windows use Developer Studio(MSDEV)\n");
199   MPI_Abort(PETSC_COMM_WORLD,1);
200 #else
201   ierr = PetscGetDisplay(display,128);CHKERRQ(ierr);
202   ierr = PetscGetProgramName(program,PETSC_MAX_PATH_LEN);CHKERRQ(ierr);
203   if (ierr) {
204     (*PetscErrorPrintf)("Cannot determine program name\n");
205     PetscFunctionReturn(1);
206   }
207   if (!program[0]) {
208     (*PetscErrorPrintf)("Cannot determine program name\n");
209     PetscFunctionReturn(1);
210   }
211   child = (int)fork();
212   if (child < 0) {
213     (*PetscErrorPrintf)("Error in fork() attaching debugger\n");
214     PetscFunctionReturn(1);
215   }
216 
217   /*
218       Swap role the parent and child. This is (I think) so that control c typed
219     in the debugger goes to the correct process.
220   */
221   if (child) child = 0;
222   else       child = (int)getppid();
223 
224   if (child) { /* I am the parent, will run the debugger */
225     const char *args[10];
226     char       pid[10];
227     PetscInt   j,jj;
228     PetscBool  isdbx,isidb,isxldb,isxxgdb,isups,isxdb,isworkshop,isddd,iskdbg,islldb;
229 
230     ierr = PetscGetHostName(hostname,64);CHKERRQ(ierr);
231     /*
232          We need to send a continue signal to the "child" process on the
233        alpha, otherwise it just stays off forever
234     */
235 #if defined(PETSC_NEED_KILL_FOR_DEBUGGER)
236     kill(child,SIGCONT);
237 #endif
238     sprintf(pid,"%d",child);
239 
240     ierr = PetscStrcmp(PetscDebugger,"xxgdb",&isxxgdb);CHKERRQ(ierr);
241     ierr = PetscStrcmp(PetscDebugger,"ddd",&isddd);CHKERRQ(ierr);
242     ierr = PetscStrcmp(PetscDebugger,"kdbg",&iskdbg);CHKERRQ(ierr);
243     ierr = PetscStrcmp(PetscDebugger,"ups",&isups);CHKERRQ(ierr);
244     ierr = PetscStrcmp(PetscDebugger,"xldb",&isxldb);CHKERRQ(ierr);
245     ierr = PetscStrcmp(PetscDebugger,"xdb",&isxdb);CHKERRQ(ierr);
246     ierr = PetscStrcmp(PetscDebugger,"dbx",&isdbx);CHKERRQ(ierr);
247     ierr = PetscStrcmp(PetscDebugger,"idb",&isidb);CHKERRQ(ierr);
248     ierr = PetscStrcmp(PetscDebugger,"workshop",&isworkshop);CHKERRQ(ierr);
249     ierr = PetscStrcmp(PetscDebugger,"lldb",&islldb);CHKERRQ(ierr);
250 
251     if (isxxgdb || isups || isddd) {
252       args[1] = program; args[2] = pid; args[3] = "-display";
253       args[0] = PetscDebugger; args[4] = display; args[5] = 0;
254       printf("PETSC: Attaching %s to %s %s on %s\n",args[0],args[1],pid,hostname);
255       if (execvp(args[0],(char**)args)  < 0) {
256         perror("Unable to start debugger");
257         exit(0);
258       }
259     } else if (iskdbg) {
260       args[1] = "-p"; args[2] = pid; args[3] = program;  args[4] = "-display";
261       args[0] = PetscDebugger; args[5] = display; args[6] = 0;
262       printf("PETSC: Attaching %s to %s %s on %s\n",args[0],args[3],pid,hostname);
263       if (execvp(args[0],(char**)args)  < 0) {
264         perror("Unable to start debugger");
265         exit(0);
266       }
267     } else if (isxldb) {
268       args[1] = "-a"; args[2] = pid; args[3] = program;  args[4] = "-display";
269       args[0] = PetscDebugger; args[5] = display; args[6] = 0;
270       printf("PETSC: Attaching %s to %s %s on %s\n",args[0],args[1],pid,hostname);
271       if (execvp(args[0],(char**)args)  < 0) {
272         perror("Unable to start debugger");
273         exit(0);
274       }
275     } else if (isworkshop) {
276       args[1] = "-s"; args[2] = pid; args[3] = "-D"; args[4] = "-";
277       args[0] = PetscDebugger; args[5] = pid; args[6] = "-display"; args[7] = display; args[8] = 0;
278       printf("PETSC: Attaching %s to %s on %s\n",args[0],pid,hostname);
279       if (execvp(args[0],(char**)args)  < 0) {
280         perror("Unable to start debugger");
281         exit(0);
282       }
283     } else {
284       j = 0;
285       if (Xterm) {
286         PetscBool cmp;
287         char      *tmp,*tmp1;
288         ierr = PetscStrncmp(DebugTerminal,"screen",6,&cmp);CHKERRQ(ierr);
289         if (!cmp) {ierr = PetscStrncmp(DebugTerminal,"gnome-terminal",6,&cmp);CHKERRQ(ierr);}
290         if (cmp) display[0] = 0; /* when using screen, we never pass -display */
291         args[j++] = tmp = DebugTerminal;
292         if (display[0]) {
293           args[j++] = "-display"; args[j++] = display;
294         }
295         while (*tmp) {
296           ierr = PetscStrchr(tmp,' ',&tmp1);CHKERRQ(ierr);
297           if (!tmp1) break;
298           *tmp1     = 0;
299           tmp       = tmp1+1;
300           args[j++] = tmp;
301         }
302       }
303       args[j++] = PetscDebugger;
304       jj = j;
305       args[j++] = program; args[j++] = pid; args[j++] = 0;
306 
307       if (isidb) {
308         j = jj;
309         args[j++] = "-pid";
310         args[j++] = pid;
311         args[j++] = "-gdb";
312         args[j++] = program;
313         args[j++] = 0;
314       }
315       if (islldb) {
316         j = jj;
317         args[j++] = "-p";
318         args[j++] = pid;
319         args[j++] = 0;
320       }
321       if (isdbx) {
322         j = jj;
323 #if defined(PETSC_USE_P_FOR_DEBUGGER)
324         args[j++] = "-p";
325         args[j++] = pid;
326         args[j++] = program;
327 #elif defined(PETSC_USE_LARGEP_FOR_DEBUGGER)
328         args[j++] = "-l";
329         args[j++] = "ALL";
330         args[j++] = "-P";
331         args[j++] = pid;
332         args[j++] = program;
333 #elif defined(PETSC_USE_A_FOR_DEBUGGER)
334         args[j++] = "-a";
335         args[j++] = pid;
336 #elif defined(PETSC_USE_PID_FOR_DEBUGGER)
337         args[j++] = "-pid";
338         args[j++] = pid;
339         args[j++] = program;
340 #else
341         args[j++] = program;
342         args[j++] = pid;
343 #endif
344         args[j++] = 0;
345       }
346       if (Xterm) {
347         if (display[0]) printf("PETSC: Attaching %s to %s of pid %s on display %s on machine %s\n",PetscDebugger,program,pid,display,hostname);
348         else            printf("PETSC: Attaching %s to %s on pid %s on %s\n",PetscDebugger,program,pid,hostname);
349 
350         if (execvp(args[0],(char**)args)  < 0) {
351           perror("Unable to start debugger in xterm");
352           exit(0);
353         }
354       } else {
355         printf("PETSC: Attaching %s to %s of pid %s on %s\n",PetscDebugger,program,pid,hostname);
356         if (execvp(args[0],(char**)args)  < 0) {
357           perror("Unable to start debugger");
358           exit(0);
359         }
360       }
361     }
362   } else {   /* I am the child, continue with user code */
363     sleeptime = 10; /* default to sleep waiting for debugger */
364     ierr = PetscOptionsGetReal(NULL,NULL,"-debugger_pause",&sleeptime,NULL);CHKERRQ(ierr);
365     if (sleeptime < 0) sleeptime = -sleeptime;
366 #if defined(PETSC_NEED_DEBUGGER_NO_SLEEP)
367     /*
368         HP cannot attach process to sleeping debugger, hence count instead
369     */
370     {
371       PetscReal x = 1.0;
372       int       i =10000000;
373       while (i--) x++;  /* cannot attach to sleeper */
374     }
375 #elif defined(PETSC_HAVE_SLEEP_RETURNS_EARLY)
376     /*
377         IBM sleep may return at anytime, hence must see if there is more time to sleep
378     */
379     {
380       int left = sleeptime;
381       while (left > 0) left = PetscSleep(left) - 1;
382     }
383 #else
384     PetscSleep(sleeptime);
385 #endif
386   }
387 #endif
388   PetscFunctionReturn(0);
389 }
390 
391 /*@C
392    PetscAttachDebuggerErrorHandler - Error handler that attaches
393    a debugger to a running process when an error is detected.
394    This routine is useful for examining variables, etc.
395 
396    Not Collective
397 
398    Input Parameters:
399 +  comm - communicator over which error occurred
400 .  line - the line number of the error (indicated by __LINE__)
401 .  file - the file in which the error was detected (indicated by __FILE__)
402 .  message - an error text string, usually just printed to the screen
403 .  number - the generic error number
404 .  p - PETSC_ERROR_INITIAL if error just detected, otherwise PETSC_ERROR_REPEAT
405 -  ctx - error handler context
406 
407    Options Database Keys:
408 .  -on_error_attach_debugger [noxterm,dbx,xxgdb,xdb,xldb,gdb] [-display name] - Activates
409    debugger attachment
410 
411    Level: developer
412 
413    Notes:
414    By default the GNU debugger, gdb, is used.  Alternatives are lldb, dbx and
415    xxgdb,xldb (on IBM rs6000), xdb (on HP-UX).
416 
417    Most users need not directly employ this routine and the other error
418    handlers, but can instead use the simplified interface SETERR, which has
419    the calling sequence
420 $     SETERRQ(PETSC_COMM_SELF,number,p,message)
421 
422    Notes for experienced users:
423    Use PetscPushErrorHandler() to set the desired error handler.  The
424    currently available PETSc error handlers are
425 $    PetscTraceBackErrorHandler()
426 $    PetscAttachDebuggerErrorHandler()
427 $    PetscAbortErrorHandler()
428    or you may write your own.
429 
430 
431 .seealso:  PetscPushErrorHandler(), PetscTraceBackErrorHandler(),
432            PetscAbortErrorHandler()
433 @*/
434 PetscErrorCode  PetscAttachDebuggerErrorHandler(MPI_Comm comm,int line,const char *fun,const char *file,PetscErrorCode num,PetscErrorType p,const char *mess,void *ctx)
435 {
436   PetscErrorCode ierr;
437 
438   PetscFunctionBegin;
439   if (!fun) fun = "User provided function";
440   if (!mess) mess = " ";
441 
442   (*PetscErrorPrintf)("%s() line %d in %s %s\n",fun,line,file,mess);
443 
444   ierr = PetscAttachDebugger();
445   if (ierr) abort(); /* call abort because don't want to kill other MPI processes that may successfully attach to debugger */
446   PetscFunctionReturn(0);
447 }
448 
449 /*@C
450    PetscStopForDebugger - Prints a message to the screen indicating how to
451          attach to the process with the debugger and then waits for the
452          debugger to attach.
453 
454    Not Collective
455 
456    Level: developer
457 
458    Notes:
459     This is likely never needed since PetscAttachDebugger() is easier to use and seems to always work.
460 
461    Developer Notes:
462     Since this can be called by the error handler, should it be calling SETERRQ() and CHKERRQ()?
463 
464 .seealso: PetscSetDebugger(), PetscAttachDebugger()
465 @*/
466 PetscErrorCode  PetscStopForDebugger(void)
467 {
468   PetscErrorCode ierr;
469   PetscInt       sleeptime=0;
470 #if !defined(PETSC_CANNOT_START_DEBUGGER)
471   int            ppid;
472   PetscMPIInt    rank;
473   char           program[PETSC_MAX_PATH_LEN],hostname[256];
474   PetscBool      isdbx,isxldb,isxxgdb,isddd,iskdbg,isups,isxdb,islldb;
475 #endif
476 
477   PetscFunctionBegin;
478 #if defined(PETSC_CANNOT_START_DEBUGGER)
479   (*PetscErrorPrintf)("System cannot start debugger; just continuing program\n");
480 #else
481   ierr = MPI_Comm_rank(PETSC_COMM_WORLD,&rank);
482   if (ierr) rank = 0; /* ignore error since this may be already in error handler */
483   ierr = PetscGetHostName(hostname,256);
484   if (ierr) {
485     (*PetscErrorPrintf)("Cannot determine hostname; just continuing program\n");
486     PetscFunctionReturn(0);
487   }
488 
489   ierr = PetscGetProgramName(program,256);
490   if (ierr) {
491     (*PetscErrorPrintf)("Cannot determine program name; just continuing program\n");
492     PetscFunctionReturn(0);
493   }
494   if (!program[0]) {
495     (*PetscErrorPrintf)("Cannot determine program name; just continuing program\n");
496     PetscFunctionReturn(0);
497   }
498 
499   ppid = getpid();
500 
501   ierr = PetscStrcmp(PetscDebugger,"xxgdb",&isxxgdb);CHKERRQ(ierr);
502   ierr = PetscStrcmp(PetscDebugger,"ddd",&isddd);CHKERRQ(ierr);
503   ierr = PetscStrcmp(PetscDebugger,"kdbg",&iskdbg);CHKERRQ(ierr);
504   ierr = PetscStrcmp(PetscDebugger,"ups",&isups);CHKERRQ(ierr);
505   ierr = PetscStrcmp(PetscDebugger,"xldb",&isxldb);CHKERRQ(ierr);
506   ierr = PetscStrcmp(PetscDebugger,"xdb",&isxdb);CHKERRQ(ierr);
507   ierr = PetscStrcmp(PetscDebugger,"dbx",&isdbx);CHKERRQ(ierr);
508   ierr = PetscStrcmp(PetscDebugger,"lldb",&islldb);CHKERRQ(ierr);
509 
510   if (isxxgdb || isups || isddd || iskdbg) printf("[%d]%s>>%s %s %d\n",rank,hostname,PetscDebugger,program,ppid);
511   else if (isxldb) printf("[%d]%s>>%s -a %d %s\n",rank,hostname,PetscDebugger,ppid,program);
512   else if (islldb) printf("[%d]%s>>%s -p %d\n",rank,hostname,PetscDebugger,ppid);
513   else if (isdbx) {
514 #if defined(PETSC_USE_P_FOR_DEBUGGER)
515      printf("[%d]%s>>%s -p %d %s\n",rank,hostname,PetscDebugger,ppid,program);
516 #elif defined(PETSC_USE_LARGEP_FOR_DEBUGGER)
517      printf("[%d]%s>>%s -l ALL -P %d %s\n",rank,hostname,PetscDebugger,ppid,program);
518 #elif defined(PETSC_USE_A_FOR_DEBUGGER)
519      printf("[%d]%s>>%s -a %d\n",rank,hostname,PetscDebugger,ppid);
520 #elif defined(PETSC_USE_PID_FOR_DEBUGGER)
521      printf("[%d]%s>>%s -pid %d %s\n",rank,hostname,PetscDebugger,ppid,program);
522 #else
523      printf("[%d]%s>>%s %s %d\n",rank,hostname,PetscDebugger,program,ppid);
524 #endif
525   }
526 #endif /* PETSC_CANNOT_START_DEBUGGER */
527 
528   fflush(stdout); /* ignore error because may already be in error handler */
529 
530   sleeptime = 25; /* default to sleep waiting for debugger */
531   PetscOptionsGetInt(NULL,NULL,"-debugger_pause",&sleeptime,NULL); /* ignore error because may already be in error handler */
532   if (sleeptime < 0) sleeptime = -sleeptime;
533 #if defined(PETSC_NEED_DEBUGGER_NO_SLEEP)
534   /*
535       HP cannot attach process to sleeping debugger, hence count instead
536   */
537   {
538     PetscReal x = 1.0;
539     int       i =10000000;
540     while (i--) x++;  /* cannot attach to sleeper */
541   }
542 #elif defined(PETSC_HAVE_SLEEP_RETURNS_EARLY)
543   /*
544       IBM sleep may return at anytime, hence must see if there is more time to sleep
545   */
546   {
547     int left = sleeptime;
548     while (left > 0) left = sleep(left) - 1;
549   }
550 #else
551   PetscSleep(sleeptime);
552 #endif
553   PetscFunctionReturn(0);
554 }
555 
556 
557 
558