Annotation of researchv10dc/vol2/pi/feel.ms, revision 1.1

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

unix.superglobalmegacorp.com

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