Annotation of MiNT/doc/debug.doc, revision 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.