Annotation of MiNT/doc/debug.doc, revision 1.1.1.1

1.1       root        1: Writing Debuggers for MiNT
                      2: 
                      3: 
                      4: 
                      5: MiNT provides a special pseudo-drive, U:\PROC, in which processes appear
                      6: 
                      7: as files. This drive may be used by debuggers much as the Unix /proc
                      8: 
                      9: file system is used, although the specific details of implementation will
                     10: 
                     11: be different.
                     12: 
                     13: 
                     14: 
                     15: The entry for a process in U:\PROC has a name like SOMEPROG.nnn, where
                     16: 
                     17: "nnn" is a 3 digit number which is the (decimal) process ID of the process,
                     18: 
                     19: and "SOMEPROG" is the name of the process. When opening a process, only
                     20: 
                     21: the process ID matters; a process does *not* need to know another process'
                     22: 
                     23: name in order to open it, only its process id. So, for example,
                     24: 
                     25:    fd = Fopen("U:\\PROC\\FOO.032",2);
                     26: 
                     27:    fd = Fopen("U:\\PROC\\.032",2);
                     28: 
                     29: and
                     30: 
                     31:    fd = Fopen("U:\\PROC\\TCSH.032",2);
                     32: 
                     33: will all open the process with process id #32 for reading and writing,
                     34: 
                     35: regardless of the actual name of that process.
                     36: 
                     37: 
                     38: 
                     39: Also, note that a process id of -1 refers to the current process, and a
                     40: 
                     41: process id of -2 refers to the parent of the current process; thus,
                     42: 
                     43:    fd = Fopen("U:\\PROC\\.-1",2);
                     44: 
                     45: may always be used to open oneself.
                     46: 
                     47: 
                     48: 
                     49: Before a process may be debugged (or "traced") it must first be marked
                     50: 
                     51: as a traced process, and a specific process (often its parent) must
                     52: 
                     53: be marked as the "tracer", the program which will be notified when the
                     54: 
                     55: traced process receives signals.
                     56: 
                     57: 
                     58: 
                     59: (1) If the process to be traced is started with Pexec, and the high bit
                     60: 
                     61:     of the Pexec mode is set, then the child process begins
                     62: 
                     63:     as a "traced" process automatically, with its parent as tracer.
                     64: 
                     65: 
                     66: 
                     67: Example:
                     68: 
                     69:     childpid = Pexec(0x8000|100, "foo.prg", "", 0L);
                     70: 
                     71: 
                     72: 
                     73: (2) The traced process may indicate that it wishes to be traced by opening
                     74: 
                     75:     its own entry in U:\PROC and performing an Fcntl(...PTRACESFLAGS) call
                     76: 
                     77:     which enables tracing. In this case, the child's parent will become
                     78: 
                     79:     the tracer automatically. Note that if the parent is not prepared to
                     80: 
                     81:     perform debugging functions, there could be undesireable results, so
                     82: 
                     83:     the child must be *certain* that the parent wishes to debug it; for
                     84: 
                     85:     example, this is the case if the child and parent are both executing
                     86: 
                     87:     from the same program image (e.g. the child was started with the
                     88: 
                     89:     Pvfork() system call). This method of indicating tracing is quite
                     90: 
                     91:     similar to Unix's ptrace(0,...) function.
                     92: 
                     93: 
                     94: 
                     95: Example:
                     96: 
                     97:     if ((pid = Pvfork()) == 0) {
                     98: 
                     99:         short flag = 1;
                    100: 
                    101:        int fd;
                    102: 
                    103: 
                    104: 
                    105: /* here we are in the child */
                    106: 
                    107: /* open self; see notes above */
                    108: 
                    109:        fd = Fopen("U:\\PROC\\A.-1",2);
                    110: 
                    111: /* perform Fcntl */
                    112: 
                    113:        Fcntl(fd, &flag, PTRACESFLAGS);
                    114: 
                    115: /* close self, we don't need the handle any more */
                    116: 
                    117:        Fclose(fd);
                    118: 
                    119: /* now overlay the child with a new program image */
                    120: 
                    121:        Pexec(200, "foo.prg", "", 0L);
                    122: 
                    123:     }
                    124: 
                    125: 
                    126: 
                    127: (3) A process may force another process to be traced by opening that process'
                    128: 
                    129:     entry in U:\PROC, and doing an Fcntl(...PTRACESFLAGS) on it. This is
                    130: 
                    131:     the only way in which to trace a process that is not the
                    132: 
                    133:     child of the debugging process.
                    134: 
                    135: 
                    136: 
                    137: Example:
                    138: 
                    139:     short flag = 1;
                    140: 
                    141: 
                    142: 
                    143: /* open process "pid" for tracing */
                    144: 
                    145:     sprintf(name, "U:\\PROC\\A.%03d", pid);
                    146: 
                    147:     fd = Fopen(name, 2);
                    148: 
                    149:     Fcntl(fd, &flag, PTRACESFLAGS);
                    150: 
                    151: /* leave fd open so that we can read and write it... */
                    152: 
                    153: 
                    154: 
                    155: 
                    156: 
                    157: What Happens When A Traced Process Receives a Signal
                    158: 
                    159: 
                    160: 
                    161: If a traced process receives a signal (for example, a bus error), then
                    162: 
                    163: it is placed into a STOP condition and a SIGCHLD signal is sent to
                    164: 
                    165: the tracer. If the tracer performs a Pwait3 or Pwaitpid system call,
                    166: 
                    167: it can then retrieve the pid of the stopped process, and the signal which
                    168: 
                    169: caused that process to stop. For example:
                    170: 
                    171: 
                    172: 
                    173: #define WIFSTOPPED(x)  (((int)((x) & 0xFF) == 0x7F) && ((int)(((x) >> 8) & 0xFF) != 0))
                    174: 
                    175: #define WSTOPSIG(x)    ((int)(((x) >> 8) & 0xFF))
                    176: 
                    177:     unsigned long r;
                    178: 
                    179: 
                    180: 
                    181: /* 0x2 is a flag that indicates that we want to see stopped processes */
                    182: 
                    183:     r = Pwait3(0x2, 0L);
                    184: 
                    185:     if (WIFSTOPPED(r)) {
                    186: 
                    187:        pid = r >> 16;
                    188: 
                    189:        signal = WSTOPSIG(r);
                    190: 
                    191:     }
                    192: 
                    193: 
                    194: 
                    195: The tracer can then examine the stopped process' address space and registers
                    196: 
                    197: (using the Fread, Fwrite, and Fcntl system calls; see below) and/or make
                    198: 
                    199: modifications to the stopped process' state. It can then restart that
                    200: 
                    201: process with either the PTRACEGO, PTRACEFLOW, or PTRACESTEP Fcntl commands.
                    202: 
                    203: The PTRACEGO command Fcntl(fd, &sig, PTRACEGO), where "sig" is a 16 bit
                    204: 
                    205: integer, restarts the process. If sig == 0, then all pending signals in
                    206: 
                    207: the traced process are cleared; otherwise, the signal represented by
                    208: 
                    209: "sig" will be delivered to the process after it starts again. Normally,
                    210: 
                    211: this signal would be the same one which caused the process to stop. Note
                    212: 
                    213: that this second time the signal is delivered it will not cause the
                    214: 
                    215: process to stop, but rather will cause whatever action is normally associated
                    216: 
                    217: with the signal (for example, SIGKILL will kill the process).
                    218: 
                    219: 
                    220: 
                    221: PTRACEFLOW and PTRACESTEP are similar to PTRACEGO, but they also set some
                    222: 
                    223: bits in the status register of the stopped process which will cause a
                    224: 
                    225: SIGTRAP (trace trap) signal to be raised either on the next instruction
                    226: 
                    227: to be executed (PTRACESTEP) or the next branch or jump instruction to
                    228: 
                    229: be executed (PTRACEFLOW; this only works on a 68030 or 68040 processor).
                    230: 
                    231: Note that if the traced process was executing a system call at the time
                    232: 
                    233: it stopped, the trace trap signal will not take effect until the process
                    234: 
                    235: leaves the kernel. PTRACESTEP, then, makes it possible to single step
                    236: 
                    237: through the traced program.
                    238: 
                    239: 
                    240: 
                    241: 
                    242: 
                    243: Reading and Writing the Process' Address Space, and Setting Breakpoints
                    244: 
                    245: 
                    246: 
                    247: Traditional debuggers for the ST have directly accessed the address space
                    248: 
                    249: of the debugged process. This is undesireable because it assumes that
                    250: 
                    251: both processes share a common address space, something which may not be
                    252: 
                    253: the case if memory protection is enabled; also, in a virtual memory
                    254: 
                    255: system logical addresses in the child and parent may be translated
                    256: 
                    257: differently. The U:\PROC file system may be used to avoid both of these
                    258: 
                    259: difficulties. If the debugger opens the process to be debugged for
                    260: 
                    261: reading and writing, it may then use Fread and Fwrite system calls to
                    262: 
                    263: transfer data (e.g. breakpoint instructions) to and from the debugged
                    264: 
                    265: process' address space.
                    266: 
                    267: 
                    268: 
                    269: To read 100 bytes from address 0x0123456 in the address space of
                    270: 
                    271: process 12, for example, one would do:
                    272: 
                    273:     char buf[100];
                    274: 
                    275: 
                    276: 
                    277:     fd = Fopen("U:\\PROC\\A.012", 2);
                    278: 
                    279:     Fseek(0x0123456L, fd, 0);
                    280: 
                    281:     Fread(fd, 100L, buf);
                    282: 
                    283: (Note that error checking should be performed in any real application;
                    284: 
                    285: for example, the Fopen could very well fail if the process being
                    286: 
                    287: opened is another user's process.)
                    288: 
                    289: 
                    290: 
                    291: 
                    292: 
                    293: Reading and Writing the Process' Registers
                    294: 
                    295: 
                    296: 
                    297: The registers of the stopped process are also available for inspection.
                    298: 
                    299: The PPROCADDR and PCTXTSIZE Fcntl commands are used to find the
                    300: 
                    301: address of the process context block in the traced process' address
                    302: 
                    303: space; the registers of the process can then be read from the address
                    304: 
                    305: space with Fread, or written to the address space with Fwrite.
                    306: 
                    307: 
                    308: 
                    309: Example:
                    310: 
                    311:     long curprocaddr;
                    312: 
                    313:     long ctxtsize;
                    314: 
                    315:     struct context {
                    316: 
                    317:        long regs[15];  /* registers d0-d7, a0-a6 */
                    318: 
                    319:        long usp;       /* user stack pointer */
                    320: 
                    321:        short sr;       /* status register */
                    322: 
                    323:        long pc;        /* program counter */
                    324: 
                    325:        long ssp;       /* supervisor stack pointer */
                    326: 
                    327:        long tvec;      /* GEMDOS terminate vector */
                    328: 
                    329:        char fstate[216]; /* internal FPU state */
                    330: 
                    331:        long fregs[3*8];  /* registers fp0-fp7 */
                    332: 
                    333:        long fctrl[3];  /* FPCR/FPSR/FPIAR */
                    334: 
                    335: /* there are some more, undocumented, fields, in the
                    336: 
                    337:  * MiNT context structure
                    338: 
                    339:  */
                    340: 
                    341:     } c;
                    342: 
                    343: 
                    344: 
                    345: /* get the address of the process control structure */
                    346: 
                    347:     Fcntl(fd, &curprocaddr, PPROCADDR);
                    348: 
                    349: 
                    350: 
                    351: /* get the size of the process context structure.
                    352: 
                    353:  * there are 2 of these, located immediately before
                    354: 
                    355:  * the process control structure. The first one
                    356: 
                    357:  * is the one we're interested in (the second one
                    358: 
                    359:  * is used by MiNT, and should never be modified).
                    360: 
                    361:  */
                    362: 
                    363:     Fcntl(fd, &ctxtsize, PCTXTSIZE);
                    364: 
                    365: 
                    366: 
                    367: /* make curprocaddr point to the first context struct */
                    368: 
                    369:     curprocaddr -= 2*ctxtsize;
                    370: 
                    371: 
                    372: 
                    373: /* now read the context structure */
                    374: 
                    375:     Fseek(curprocaddr, fd, 0);
                    376: 
                    377:     Fread(fd, (long)sizeof(struct context), &c);
                    378: 
                    379: 
                    380: 
                    381: /* the various fields of c are now set up properly */
                    382: 
                    383:    ... /* code omitted */
                    384: 
                    385: 
                    386: 
                    387: /* now write back the context to reflect whatever changes
                    388: 
                    389:  * we made
                    390: 
                    391:  */
                    392: 
                    393:     Fseek(curprocaddr, fd, 0);
                    394: 
                    395:     Fwrite(fd, (long)sizeof(struct context), &c);
                    396: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.