|
|
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:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.