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