Diagnostic feedback
This document contains a guide to MPS diagnostic feedback, followed by the historical initial design. References, History, Copyright and License are at the end.
Readership: any MPS developer. Not confidential.
Guide
Introduction
Diagnostic feedback is information created by the MPS diagnostic system for the purpose of helping MPS programmers and client-code programmers.
Such a piece of information is called "a diagnostic". (See also Parts of the MPS diagnostic system, below).
A diagnostic is not intended to be end-user readable (or visible), or machine-parseable. A diagnostic is not intended to be stable from one release to the next: it may be modified or removed at any time.
MPS diagnostic feedback code must do these things:
- calculate, store, and propagate data;
- collate, synthesise, and format it into a human-useful diagnostic;
- control (eg. filter) output of diagnostics;
- use a channel to get the diagnostic out.
Note: the knowledge/code/logic for constructing the human-useful message is kept inside normal MPS source code. This means it is always in-sync with changes to the MPS. This also means that any external utilities used to display the messages do not need to understand, or keep in sync with, the details of what's going inside the MPS.
How to see some MPS diagnostic output
To run the MPS and get diagnostic output from it:
- Use a variety with diagnostics compiled-in. Currently, that means variety.di. See config.h.
- Check that the diagnostics you require are generated, by looking in MPS source for invocations of the appropriate macro (eg. DIAG_SINGLEF()).
- Check that the diagnostics you require will be output, by looking at the diagnostic filter rules in diag.c.
- Run the MPS and client in an environment that supports the channel used (eg. at a command-line if using WriteF).
What is a diagnostic?
A diagnostic has three parts:
- a trigger condition, that causes this diagnostic to be emitted;
- a text tag (eg "TraceStart") which is the name of this diagnostic; and
- a paragraph of human-useful text.
A diagnostic is emitted by the MPS at a certain point in time when a certain event happens.
Diagnostics are not nested. Every diagnostic must have a tag. Each diagnostic should have a unique tag (uniqueness is just to help the humans; the diagnostic system does not care).
The paragraph of text can be many lines long. It usually explains what event caused the diagnostic to be emitted, and commonly also includes the output of some <object>Describe() methods for various relevant objects. (For example, the TraceStart diagnostic might call, and include the output generated by, the TraceDescribe() method).
How do I control (filter) which diagnostics I see?
All diagnostics are emitted and then filtered according to the "diagnostic filter rules".
The first level of control is filtering by tag. (For example, only show TraceStart diagnostics).
The second level of control is filtering by paragraph content. (For example, only show TraceStart diagnostics where the trace is started because a nursery generation is full).
The third level of control is filtering by line content. (For example, only show lines containing the word "whiteSet").
See diag.c for details.
Note: the entire filtering mechanism can be turned off, so that diagnostics go immediately to mps_lib_stdout, with no buffering or filtering See diag.c#.filter-disable.
How to write a diagnostic
Improve stateless Describe methods where possible
Where possible, don't put clever code into an event-triggered diagnostic: put it into a stateless <object>Describe() method instead, and then call that method when emitting your diagnostic.
For example:
FooDescribe(Foo foo, mps_lib_FILE *stream) { /* show value of new "quux" field */ WriteF(stream, "Foo: $P { quux: $U }\n", foo, foo->quux); } FooWibble(Foo foo) { ... DIAG_FIRSTF(( "FooWibble", "Wibbling foo $P", foo, NULL)); DIAG( FooDescribe(foo, DIAG_STREAM); ); DIAG_END("FooWibble"); ... }
This is much better, because other people can use your human-useful output in their diagnostics, or 'live' in a debugger.
How to use DIAG_*F output macros
For a simple diagnostic, use DIAG_SINGLEF. This begins the tag, puts text into the paragraph, and ends the tag immediately.
For a more complex diagnostic, the first call must be DIAG_FIRSTF, which begins a diag tag.
While a tag is current, you can add text to the diagnostic's paragraph using DIAG_MOREF, and WriteF( DIAG_STREAM, ... ).
(Note: DIAG_STREAM is not a real standard C library stream. If you want stream-level access, you may use Stream_fputc() and Stream_fputs().)
End the tag by calling DIAG_END.
Compile away in non-diag varieties; no side effects
Wrap non-output code with the DIAG() and DIAG_DECL() macros, to make sure that non-diag varieties do not execute diagnostic-generating code.
For complex diagnostic-generating code, it may be cleaner to move it into a separate local function. Put "_diag" on the end of the function name (eg. "TraceStart_diag()").
Obviously, diagnostic-generating code must have no side effects.
Choosing tags
Tags should be valid C identifiers. Unless you know of a good reason why not. (Not currently checked).
There's no formal scheme for tag naming, but make it helpful and — informally — hierarchical, eg. TraceBegin, TraceStart, TraceEnd, etc. (not BeginTrace, EndTrace, ...).
Writing good paragraph text
IMPORTANT: Make your diagnostics easy to understand! Other people will read your diagnostics! Make them clear and helpful. Do not make them terse and cryptic. If you use symbols, print a key in the diagnostic. (If you don't want to see this the screen clutter, then you can always add a filter rule to your personal rule set to filter it out).
Maintaining helpful filter rules
If you add a 'noisy' diagnostic, add a rule to the default ruleset to turn it off.
How the MPS diagnostic system works
Channels
The recommended channel is WriteF to stdout.
Other possible of future channels might be:
- printf;
- a new type (yet to be defined) of mps_message;
- squirt them into the telemetry-log-events system;
- telnet.
Currently, only printf and WriteF are supported. See the DIAG_WITH_ macros in mpm.h.
You can also use a debugger to call <type>Describe() methods directly, from within the debugger.
Note: it is unfortunate that choice of channel may (for some channels) also dictate the form of the code that synthesises the message. (For example, WriteF-style parameter-expansion is not possible when using the printf channel, because there is no way to get WriteF to produce its output into a string). This is just a technical hitch; logically, the code that synthesises a diagnostic message should not care which channel will be used to transmit it out of the MPS.
Parts of the MPS diagnostic system
The following facilities are considered part of the MPS diagnostic system:
- the <type>Describe() methods.
- the DIAG* macros (DIAG, DIAG_DECL, DIAG_*F, etc);
- the STATISTIC macros (see mpm.h);
- the METER macros and meter subsystem;
Related Systems
The MPS diagnostic system is separate from the following other MPS systems:
- The telemetry-log-events system (which emits much more data, in a less human-readable form, requires MPS-aware external tools, and is more stable from release to release). In non-diagnostic telemetry varieties, the telemetry-log-events system emits events that log all normal MPS actions. In diagnostic telemetry varieties, it may emit additional events containing diagnostic information. Additionally, the telemetry-log-events stream might in future be available as a channel for emitting human-readable text diagnostics. See also design/telemetry.
- The MPS message system (which is present in all varieties, and manages asynchronous communication from the MPS to the client program). However, the MPS message system might in future also be available as a channel for emitting diagnostics. See also design/message.
Initial Design
See http://info.ravenbrook.com/mail/2007/04/13/13-07-45/0.txt: "diagnostic feedback from the MPS" by RHSK.
Diverse types of diagnostic feedback: http://info.ravenbrook.com/mail/2007/04/18/10-58-49/0.txt.
RHSK 2007-06-28
It must be possible to add, modify, or remove diagnostics to affect diagnostic varieties, without a substantial secondary effect on non-diagnostic varieties. It is not a requirement that there be zero effect.
This means:
- for actual output, you must use appropriate DIAG macros (which compile away to nothing in non-diagnostic varieties);
- for propagating and synthesising data, use appropriate DIAG macros where it is reasonably clean to do so, such as for local variables and statements used only for diagnostic output;
- where it is clearer, and without substantial secondary effect, to include code (such as additional function parameters) in all varieties, that's okay.
Note: the MPS diagnostic feedback system was initially developed in mps/branch/2007-04-18/diag, mps/branch/2007-07-19/gcdiag, and mps/branch/2007-08-07/diagtag.
A. References
B. Document History
2007-06-28 | RHSK | Create. |
2007-06-28 | RHSK | Telemetry-log-events system is a possible channel. |
2007-06-29 | RHSK | Feedback (not output), each "a diagnostic". Parts of the system; related systems. Link to initial design email. |
2007-08-14 | RHSK | (Diag Filtering). Expand section: How to see some MPS diagnostic output, with what a diagnostic is, and how to filter it. New section: How to write a diagnostic. Various minor updates and corrections. |
C. Copyright and License
This document is copyright © 2007 Ravenbrook Limited. All rights reserved. This is an open source license. Contact Ravenbrook for commercial licensing options.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Redistributions in any form must be accompanied by information on how to obtain complete source code for the this software and any accompanying software that uses this software. The source code must either be included in the distribution or be available for no more than the cost of distribution plus a nominal fee, and must be freely redistributable under reasonable conditions. For an executable file, complete source code means the source code for all modules it contains. It does not include source code for modules or files that typically accompany the major components of the operating system on which the executable file runs.
This software is provided by the copyright holders and contributors "as is" and any express or implied warranties, including, but not limited to, the implied warranties of merchantability, fitness for a particular purpose, or non-infringement, are disclaimed. In no event shall the copyright holders and contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage.