|
|
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.