|
|
1.1 ! root 1: .so ../ADM/mac ! 2: .XX pi 335 "The Feel of Pi" ! 3: ...\" Wed May 21 15:35:31 EDT 1986 ! 4: .ds p \f2pi\fP ! 5: .ds P \f2Pi\fP ! 6: .ds j \f2jim\fP ! 7: .ds J \f2Jim\fP ! 8: .TL ! 9: The Feel of Pi ! 10: .AU ! 11: T. A. Cargill ! 12: .AI ! 13: .MH ! 14: .AB ! 15: .PP ! 16: \*P is an interactive debugger for C and C++ on Eighth Edition ! 17: .UX ! 18: systems. ! 19: Its user interface uses multiple windows on a DMD 5620 terminal. ! 20: \*P does not feel like a debugger with a sequential command language, nor does it ! 21: feel like a debugger where commands from a bitmap display are translated into a ! 22: sequential command language. ! 23: In contrast, \*p's multiple windows display multiple active views of its multiple ! 24: subject processes, allowing the programmer to browse through a network of ! 25: information. ! 26: The programmer interactively explores a set of executing processes, ! 27: probing for insight with a tool that really helps. ! 28: .PP ! 29: Each window displays a specific view of a subject process in parallel with ! 30: the other windows. ! 31: The contents of pop-up menus are determined by context; ! 32: the current window and the line of text selected within it. ! 33: .PP ! 34: \*P is written in C++ and uses Eighth Edition's ! 35: .CW /proc ! 36: to access arbitrary live subject processes. ! 37: .AE ! 38: .SH ! 39: Introduction ! 40: .PP ! 41: \*P (Process Inspector) is an experiment in debugging with an interactive, bitmap ! 42: graphics user interface. ! 43: The debugging technology is conventional: breakpoints are planted in the subject ! 44: process so that the states the process moves through may be examined. ! 45: But the user interface is unconventional. ! 46: .PP ! 47: In a conventional debugger, the programmer inputs a sequence of commands that are ! 48: interpreted by the debugger. ! 49: The debugger responds with information about the subject process. ! 50: Several problems arise. ! 51: First, the debugger can usually accept only the subset of its commands ! 52: applicable to the debugger's current state. ! 53: For example, breakpoints can only be set in the current source file, or ! 54: expressions can only be evaluated in the current activation record. ! 55: Second, the debugger's output is passive and cannot be used to obtain ! 56: further information about, or other views of, the process. ! 57: For example, if a value is displayed by some command in an ! 58: inappropriate format, the programmer must re-issue the command, specifying another ! 59: format, or take the value and manipulate it elsewhere. ! 60: The effect is that any non-trivial debugging is accomplished by combining ! 61: the debugger with some of our oldest tools \- pencil and paper. ! 62: Third, a debugging command language must necessarily be very large, if it ! 63: is to be useful. ! 64: Generally, keyboard languages are complicated, and often cryptic. ! 65: .PP ! 66: The goal in writing \*p was to create a full-function interactive debugger with a ! 67: good user interface: ! 68: menu-driven, reactive, usable without a scratch pad or reference manual. ! 69: .SH ! 70: Interface Model ! 71: .PP ! 72: \*P's user interface assigns each view of a subject process to a ! 73: separate window. ! 74: Each window has its own menu of operations, appropriate to the view presented. ! 75: Within each window are lines of text providing details of the window's view. ! 76: Each line has its own menu of operations, appropriate to the information presented. ! 77: Interaction is driven by the programmer selecting operations from these menus. ! 78: In response to each operation, the debugger adds or removes windows, or lines, ! 79: or their menus. ! 80: A window or line may also choose to accept a line of input from the keyboard. ! 81: .PP ! 82: On the DMD 5620, a layer is subdivided into a set of scrolling, overlapping ! 83: windows. ! 84: The mechanics of the user interface are derived from \*j, a text editor by ! 85: Pike|reference(blit bstj) |reference(latest volume1). ! 86: There is a current window (with a heavy border), and within it a current ! 87: line (video-inverted). ! 88: Each button on the three-button mouse serves a specific role. ! 89: Button number 1 is for pointing. ! 90: If the cursor is outside the current window, button 1 selects a new current window. ! 91: If the cursor is inside and over a line of text, that line becomes current. ! 92: If inside and in the scroll zone, the window ! 93: scrolls to center the proportional scroll bar over the cursor. ! 94: Buttons 2 and 3 raise the pop-up menus for the current line and window, ! 95: respectively. ! 96: Menus also scroll and may have pop-up sub-menus, making large menus relatively ! 97: easy to use. ! 98: .SH ! 99: An Example ! 100: .PP ! 101: I will demonstrate \*p by examining the copy of \*j that I am ! 102: using to write this paper. ! 103: \*J is two processes, one in the host computer and one in the terminal. ! 104: I will work with its host process. ! 105: I create a new layer on my 5620's screen and simply invoke \*p: ! 106: .P1 ! 107: pi ! 108: .P2 ! 109: After about 20K bytes of user interface code has downloaded into the 5620, \*p's ! 110: cursor icon requests me to sweep a rectangle for a new window \- the ``Pi'' ! 111: window, the master window through which \*p may be bound dynamically to processes ! 112: and core dumps. ! 113: I now have one (almost empty) window in \*p's layer: ! 114: .MB binps ! 115: .LP ! 116: Selecting ! 117: .CW /bin/ps ' ` ! 118: from this window's menu runs the ! 119: .I ps ! 120: command and lists the output in the window, one process per line: ! 121: .MB jim ! 122: .LP ! 123: It shows me with a light load \- I am only editing. ! 124: To examine \*j, I point to process 10918 in this list and select ! 125: .CW open\ process ' ` ! 126: from its menu. ! 127: I am now requested to sweep a ``Process'' window. ! 128: The Process window has overall control of the process and can create windows ! 129: with more detailed views. ! 130: The process window shows the state of the process, and a callstack if ! 131: the process is stopped. ! 132: The state of process 10918 is: ! 133: .MB RUN1 ! 134: .LP ! 135: This is the usual state for \*j's host process ! 136: \- it is blocked reading from the terminal. ! 137: \*P polls the state of the process every second and updates the Process window ! 138: asynchronously with respect to the user and the subject process. ! 139: After some more editing the consumed processor time has increased.. ! 140: I raise the Process window's menu: ! 141: .MB srctext ! 142: .LP ! 143: .CW stop ' ` ! 144: stops the process asynchronously. ! 145: .CW run ' ` ! 146: restarts it. ! 147: .CW src\ text ' ` ! 148: creates windows for viewing source text. ! 149: .CW Globals ' ` ! 150: creates a window for evaluating expressions in global scope. ! 151: .CW RawMemory ' ` ! 152: creates a ``memory editor,'' in which uninterpreted memory cells may be viewed ! 153: and modified. ! 154: .CW Assembler ' ` ! 155: creates a window that disassembles memory and provides instruction level ! 156: operations. ! 157: .CW Signals ' ` ! 158: creates a window that monitors signals to the process. ! 159: .CW kill? ' ` ! 160: kills the process; the question mark calls for a confirming button hit. ! 161: .CW Journal ' ` ! 162: creates a window that records significant events in the process \- a trace. ! 163: .PP ! 164: First, I choose to look at some source text. ! 165: If there were a single source file, ! 166: .CW src\ text ' ` ! 167: would create a ``Source Text'' window for it. ! 168: \*J has several source files; so \*p asks me to sweep a ! 169: ``Source Files'' window that lists them: ! 170: .MB srcfiles ! 171: .LP ! 172: I point to ! 173: .CW pattern.c ! 174: and choose ! 175: .CW open\ source\ file ' ` ! 176: from its menu. ! 177: I sweep a Source Text window. ! 178: It fills with the first few lines of ! 179: .CW pattern.c . ! 180: I raise its menu: ! 181: .MB index ! 182: .LP ! 183: (I have not looked at this code before starting to write this example. ! 184: I believe I will find \*j's regular expression pattern matcher here. ! 185: I know no details of its implementation. ! 186: It is as if I were starting from scratch to find a bug in Pike's code.) ! 187: Moving the cursor over the arrow at the right of ! 188: .CW index\ by\ fcn ' ` ! 189: pops up a sub-menu that is a table of contents by function (with line number) of ! 190: .CW pattern.c : ! 191: .MB compile ! 192: It suggests, as I expected, that \*j compiles regular expressions into ! 193: a representation from which they can be interpreted efficiently. ! 194: To see some of this code, I select ! 195: .CW compile().......79 '. ` ! 196: This scrolls the window so that the line with the opening brace of ! 197: .CW compile() ! 198: is in the center: ! 199: .MB setbpt ! 200: .LP ! 201: To set a breakpoint, I point to a line of source text, say the opening brace, and ! 202: select ! 203: .CW set\ bpt ' ` ! 204: from its menu. ! 205: To indicate the breakpoint, ! 206: .CW >>> ' ` ! 207: appears at the beginning of the source line: ! 208: .MB gtgtgt ! 209: .LP ! 210: Note that the breakpoint was set while \*j executed asynchronously. ! 211: .PP ! 212: To force \*j to execute the breakpoint, I type (in \*j's layer) a search command ! 213: whose pattern matches a non-empty sequence of ! 214: .CW a ' ` ! 215: followed by a non-empty sequence of ! 216: .CW b ': ` ! 217: .CW /a+b+ . ! 218: When \*j hits the breakpoint, \*p asynchronously notices its change of state and ! 219: reports it in the Process window, along with as much of the callstack as fits ! 220: (here, only the deepest activation record): ! 221: .MB bpt ! 222: .LP ! 223: In the Source Text window, the breakpoint source line is ! 224: selected to show the current context. ! 225: To see more of the callstack I reshape the Process window, making it larger: ! 226: .MB callstack ! 227: .LP ! 228: To see the context from which ! 229: .CW compile() ! 230: was called, I select the ! 231: .CW commands(f=0xBCAC) ! 232: line from the callstack and choose ! 233: .CW show\ jim.c:368 ' ` ! 234: from its menu. ! 235: I am prompted to sweep another Source Text window, ! 236: .CW jim.c , ! 237: to see this context. ! 238: To catch the process before it calls ! 239: .CW execute() , ! 240: I change the selection from the line ! 241: .P1 ! 242: compile(p, TRUE); ! 243: .P2 ! 244: to the ! 245: .CW if ! 246: statement four lines below ! 247: and set a breakpoint: ! 248: .MB jimc ! 249: .LP ! 250: I then ! 251: .CW run ' ` ! 252: from the Source Text window's menu: ! 253: .MB run ! 254: .LP ! 255: .PP ! 256: When \*j reaches this breakpoint, I choose ! 257: .CW step\ into\ fcn ' ` ! 258: from the same menu to step the process into ! 259: .CW execute() . ! 260: (The other source stepping commands step ! 261: .I over ! 262: called functions.) ! 263: The source context for ! 264: .CW execute() ! 265: is back in the first source file, ! 266: .CW pattern.c . ! 267: .CW pattern.c 's ! 268: Source Text window moves to the front of the screen and highlights the ! 269: opening brace of ! 270: .CW execute() : ! 271: .MB step1stmt ! 272: .LP ! 273: It appears that the real work will be done by ! 274: .CW fexecute() . ! 275: I could set a breakpoint there, but I use ! 276: .CW step\ 1\ stmt ' ` ! 277: from the source window's menu a few times until I get to: ! 278: .P1 ! 279: return fexecute(f); ! 280: .P2 ! 281: and then use ! 282: .CW step\ into\ fcn ' ` ! 283: again. ! 284: The context shown from ! 285: .CW pattern.c ! 286: changes: ! 287: .MB fexec ! 288: .LP ! 289: .PP ! 290: .CW fexecute() ! 291: looks non-trivial. ! 292: Before going further, I would like to understand the data structure driving it. ! 293: I do not know what this data structure is. ! 294: Looking forward through the source text of ! 295: .CW fexecute() ! 296: I understand very little of the code. ! 297: But three lines do make sense: ! 298: .P1 ! 299: /* fast check for first char */ ! 300: if(startchar && *s!=startchar) ! 301: goto Continue; ! 302: .P2 ! 303: Surely, ! 304: .CW startchar ! 305: holds a literal character and ! 306: .CW s ! 307: is a pointer into a scanned string. ! 308: To test this I set a breakpoint on the ! 309: .CW if ! 310: and ! 311: .CW run '. ` ! 312: At the breakpoint I need the value of ! 313: .CW startchar . ! 314: Choosing ! 315: .CW open\ frame ' ` ! 316: from the source line's menu: ! 317: .MB openframe ! 318: .LP ! 319: creates a ``Frame'' window for the activation record of the function ! 320: corresponding to the source line. ! 321: A Frame window evaluates expressions with respect to its activation record. ! 322: The menu contains local variables, each flagged as an argument, an automatic or ! 323: a register: ! 324: .MB startchar ! 325: .LP ! 326: Choosing ! 327: .CW startchar ! 328: evaluates that expression: ! 329: .MB asciion ! 330: .LP ! 331: Is that an ! 332: .CW a '? ` ! 333: The value is in decimal because ! 334: .CW startchar ! 335: is declared ! 336: .CW int . ! 337: To override the default format, I select ! 338: .CW format ' ` ! 339: from the expression's menu, and ! 340: .CW ascii\ on ' ` ! 341: from the sub-menu. ! 342: The expression re-displays itself: ! 343: .MB a97 ! 344: .LP ! 345: .PP ! 346: The value of ! 347: .CW startchar ! 348: looks right and probably came from the data structure I am after. ! 349: Scrolling back a few lines in ! 350: .CW pattern.c ! 351: I find an assignment to ! 352: .CW startchar : ! 353: .MB assign ! 354: .LP ! 355: .CW fstart ! 356: may be the pointer I need, but it does not appear in ! 357: .CW fexecute() 's ! 358: menu. ! 359: It must be a global. ! 360: Rather than open the global expression evaluator window and look in its menu, ! 361: I enter the expression ! 362: .P1 ! 363: fstart ! 364: .P2 ! 365: from the keyboard, with ! 366: .CW fexecute() 's ! 367: Frame window selected as the target. ! 368: The Frame window now contains two expressions: ! 369: .in +.2i ! 370: .MB typeof ! 371: .LP ! 372: .in -.2i ! 373: What type is ! 374: .CW fstart ? ! 375: I can almost tell from its menu. ! 376: Most of the entries in an expression's menu are new expressions that may ! 377: be derived from it. ! 378: The ! 379: .CW $-> 's ! 380: tell me that I have a pointer to a structure. ! 381: (In the menu, and from the keyboard, ! 382: .CW $ ! 383: denotes the current expression.) ! 384: Choosing ! 385: .CW typeof\ $ ' ` ! 386: confirms it: ! 387: .in +.2i ! 388: .MB left ! 389: .in -.2i ! 390: .LP ! 391: Choosing ! 392: .CW $->left ', ` ! 393: followed by ! 394: .CW $->op ', ` ! 395: and ! 396: .CW $->right ' ` ! 397: yields: ! 398: .MB op ! 399: .LP ! 400: Reformatting ! 401: .CW fstart->op ! 402: in ASCII leaves: ! 403: .in +.2i ! 404: .MB star ! 405: .LP ! 406: .in -.2i ! 407: .LP ! 408: So here is some kind of tree, where an operator code ! 409: less than octal 200 is to match its own value in the scanned text. ! 410: The left sub-tree is empty; the right looks promising. ! 411: Dereferencing with ! 412: .CW *\ $ ' ` ! 413: yields: ! 414: .sp 0.5 ! 415: .in +.2i ! 416: .MB right ! 417: .in -.2i ! 418: The ! 419: .CW left ! 420: field of ! 421: .CW fstart->right ! 422: is equal to ! 423: .CW fstart ! 424: itself; maybe this is a doubly-linked list. ! 425: Applying ! 426: .CW $->right ' ` ! 427: to ! 428: .CW fstart->right , ! 429: I get: ! 430: .MB rr ! 431: .LP ! 432: I already know this, but applying ! 433: .CW *\ $ ' ` ! 434: produces (showing \*p's entire layer for the first time): ! 435: .MB thelast ! 436: .LP ! 437: Note that the value of the ! 438: .CW op ! 439: field for the current expression is displayed in ASCII as ! 440: .CW b '. ` ! 441: The ASCII format explicitly requested for that field earlier was saved in the ! 442: symbol table and is now the default. ! 443: The ! 444: .CW left ! 445: pointer is zero here. ! 446: It now looks as though ! 447: .CW left ! 448: points back to the beginning of the sub-pattern controlled by the ! 449: closure operator. ! 450: .PP ! 451: Let me stop here. ! 452: I have started to unravel the data structure and understand the program. ! 453: I hope this paper description conveys something of the feel of \*p. ! 454: .SH ! 455: Programmer Reaction ! 456: .PP ! 457: Most programmers take somewhere from a few hours to a few days to make the ! 458: transition from drowning in a sea of windows to considering \*p an indispensable ! 459: tool. ! 460: At the outset, they do not expect dynamic binding to subject processes and ! 461: cannot see why there are so many windows. ! 462: Invoking a debugger without specifying a dump or program is a foreign notion. ! 463: Expectations of a debugger are very low: ``I only want the value of ! 464: .I x ! 465: when ! 466: .I f() ! 467: is called \- why all the windows?'' ! 468: With increased confidence and ambition they use \*p with more sophistication. ! 469: Styles vary considerably. ! 470: Each programmer uses idiosyncratic sizes, shapes and placements of windows, ! 471: especially when debugging multiple processes. ! 472: Some prefer to enter most of their expressions from the keyboard, others ! 473: never touch it. ! 474: .PP ! 475: There are two main problems. ! 476: First, binding \*p to subject processes is too complicated for novices. ! 477: Experts demand many special facilities, which have been allowed to complicate ! 478: what the novice encounters. ! 479: Second, demand for programmable debugging is growing among the expert users. ! 480: Programmability was excluded from \*p in order to concentrate on ! 481: interactive behavior. ! 482: \*P does have ``spy'' expressions, which re-display themselves if their values ! 483: change, and conditional breakpoints, but it is not programmable, say, to ! 484: step 10 instructions after encountering a breakpoint. ! 485: It is now time to think about how programmability and interaction can be combined. ! 486: .SH ! 487: Asynchronous Multiple Processes ! 488: .PP ! 489: An arbitrary set of processes may be examined simultaneously. ! 490: For each subject process there is an independent network of windows. ! 491: Since all the windows are in a flat space on the screen, each successive action ! 492: from the programmer may be in an any window, associated with an any ! 493: process. ! 494: Events in the set of subject processes are reported as they occur. ! 495: For example, the programmer might step source statements alternately ! 496: between a pair of processes while watching the changing values of ! 497: spy expressions in a third process. ! 498: This simplifies debugging situations that were difficult or impossible ! 499: in the past. ! 500: For example, it becomes straightforward to ! 501: (i) compare the behavior of two similar programs; ! 502: (ii) compare the effects of different inputs on a single program; ! 503: (iii) observe the interaction between related processes, say child and parent. ! 504: .SH ! 505: Implementation ! 506: .PP ! 507: \*P depends on the Research ! 508: .UX 's ! 509: .CW /proc |reference(killian processes)|reference(latest volume1), ! 510: and object-oriented programming in C++|reference(cplusplus). ! 511: .PP ! 512: .CW /proc ! 513: permits \*p to bind itself dynamically to any processes, and execute ! 514: asynchronously with them. ! 515: For each process, \*p can tell the kernel how to handle an ! 516: .I exec() ! 517: by the process and signals received from other processes. ! 518: A breakpoint in code executed by a child of a subject process suspends the child ! 519: so that it may be opened and examined. ! 520: Code sharing is managed transparently by ! 521: .CW /proc . ! 522: .PP ! 523: The browsing and asynchrony are driven by object-oriented programming in C++. ! 524: A large host C++ program communicates with a small 5620 C program. ! 525: Everything the programmer can identify on the screen is a C++ object, an ! 526: instance of a class. ! 527: The host program binds an object identifier (which can be thought ! 528: of as a host address) and a menu of operations ! 529: to each window and each line of text as it describes them to the terminal. ! 530: When the programmer selects an operation from a menu associated with an object's ! 531: image, the terminal sends back a remote invocation of one of the object's member ! 532: functions. ! 533: Generally, executing this function creates, changes or removes host objects and ! 534: their images in the terminal. ! 535: Host-terminal communication is asynchronous; the programmer need not wait ! 536: for results to appear on the screen before issuing another operation. ! 537: There is no ambiguity in this ``mouse-ahead''; the identity of the object ! 538: on which a menu operates is frozen when the menu is raised. ! 539: A crude object registration scheme in the host detects (with high probability) ! 540: and ignores operations for objects that have been destroyed. ! 541: .SH ! 542: Conclusion ! 543: .PP ! 544: \*P's easy access to information about arbitrary processes ! 545: has made programmers more sophisticated in their debugging practices. ! 546: Programmers working with large programs written by others are happier. ! 547: Programmers who would not normally read assembly code can sometimes spot ! 548: code generation bugs in the compiler. ! 549: Programmers with families of interacting processes have a handle on them. ! 550: In general, programmers understand their programs better. ! 551: .SH ! 552: References ! 553: .LP ! 554: |reference_placement ! 555: .BP ! 556: photo page ! 557: .BP ! 558: divider with title ! 559: .sp ! 560: .ce ! 561: Supporting Tools and Languages
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.