mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-24 06:20:43 -08:00
773 lines
21 KiB
C
773 lines
21 KiB
C
/* diag.c: MEMORY POOL MANAGER DIAGNOSTICS
|
|
*
|
|
* $Id$
|
|
* Copyright (c) 2007 Ravenbrook Limited. See end of file for license.
|
|
*
|
|
* To Do: [RHSK 2007-08-13]
|
|
* @@ sigs and AVERTs for Rule, and macro for Rules initializer
|
|
* @@ deprecate un-tagged diags, remove old macros
|
|
* @@ every diag should end with \n: warn if this is missing.
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include "mpm.h"
|
|
#include "mpslib.h" /* for mps_lib_stdout */
|
|
|
|
#if defined(DIAG_WITH_STREAM_AND_WRITEF)
|
|
|
|
typedef struct RuleStruct {
|
|
const char *action;
|
|
const char *tag;
|
|
const char *para;
|
|
const char *line;
|
|
int tpMatch; /* .tpmatch */
|
|
/* @@ needs sig; (at end, to make initializer expression easy?) */
|
|
} *Rule;
|
|
|
|
|
|
/* RulesGlobal -- throw away some diags (see INSTRUCTIONS below) */
|
|
|
|
struct RuleStruct RulesGlobal[] = {
|
|
{ "-", "*", "*", "*" },
|
|
{ "+", "DiagFilter_Rules", "*", "*" },
|
|
{ "+", "VMCompact", "*", "*" },
|
|
/* ----v---- always on please (RHSK) ----v---- */
|
|
{ "+", "MPSVersion", "*", "*" },
|
|
{ "+", "traceSetSignalEmergency", "*", "*" },
|
|
{ NULL, "", "", "" }
|
|
};
|
|
|
|
struct RuleStruct RulesGlobal_RHSK[] = {
|
|
{ "+", "*", "*", "*" },
|
|
{ "+", "DiagFilter_Rules", "*", "*" },
|
|
{ "-", "DIAGTEST_", "*", "*" },
|
|
{ "+", "AMCTraceEnd_pageret", "*", "*" },
|
|
{ "-", "ChainCondemnAuto", "*", "*" },
|
|
{ "+", "VM_ix_", "*", "*" },
|
|
{ "-", "vmArenaExtend_", "*", "*" },
|
|
{ "-", "traceFindGrey", "*", "*" },
|
|
{ "-", "TraceStart", "*", "*" },
|
|
{ "+", "TraceStart", "*", "controlPool" },
|
|
{ "+", "TraceStart", "*", "reserved" },
|
|
{ "+", "TraceStart", "*", "committed" },
|
|
{ "+", "TraceStart", "*", "genZoneSet" },
|
|
{ "-", "TraceStart", "because code 1", "*" },
|
|
{ "+", "VMCompact", "*", "*" },
|
|
{ "-", "VMCompact_hex", "*", "*" },
|
|
{ "+", "VM_ix_Create", "*", "*" },
|
|
/* ----v---- always on please (RHSK) ----v---- */
|
|
{ "+", "traceSetSignalEmergency", "*", "*" },
|
|
{ NULL, "", "", "" }
|
|
};
|
|
|
|
struct RuleStruct RulesGlobalExample[] = {
|
|
{ "+", "*", "*", "*" },
|
|
{ "+", "DiagFilter_Rules", "*", "*" },
|
|
{ "-", "DIAGTEST_", "*", "*" },
|
|
{ "+", "ChainCondemnAuto", "gens [0..0]", "*" },
|
|
{ "+", "TraceStart", "*", "*" },
|
|
{ "+", "TraceStart", "because code 1:", "*" },
|
|
{ "-", "TraceStart", "*", "controlPool" },
|
|
{ "-", "TraceStart", "*", "ommit" },
|
|
{ "-", "TraceStart", "*", "zoneShift" },
|
|
{ "-", "TraceStart", "*", "alignment" },
|
|
{ "-", "amcScanNailed-loop", "*", "*" },
|
|
{ NULL, "", "", "" }
|
|
};
|
|
|
|
/* RulesGlobal -- INSTRUCTIONS
|
|
*
|
|
* In your local copy of diag.c, you can modify RulesGlobal as you
|
|
* wish, to control what diags you see.
|
|
*
|
|
* Each rule consists of: action, tag, para, and line. A rule that
|
|
* matches on TAG, PARA and LINE determines what ACTION is taken
|
|
* for that line of that diag. Later rules override earlier rules,
|
|
* ie. the lowest matching rule wins. (And at least one rule must
|
|
* match, so the first rule should be a catch-all).
|
|
*
|
|
* ACTION = "+" (output this line of diag), or "-" (skip this line).
|
|
*
|
|
* TAG: does pattern (text or *) appear in diag's tag?
|
|
*
|
|
* PARA: does pattern (text or *) appear anywhere in diag's text output
|
|
* (does not match the tag)?
|
|
*
|
|
* LINE: does pattern (text or *) appear on this line of the diag
|
|
* text?
|
|
*
|
|
* Note: a diag that deliberately has no output, eg.
|
|
* DIAG_SINGLEF(( "MyTag", NULL )),
|
|
* is treated as having a single empty 'line'. See .empty-diag.
|
|
*
|
|
* Note: for help debugging your ruleset, see .rules.debug below.
|
|
*
|
|
* Note: the entire filtering mechanism can be turned off, so that
|
|
* diagnostics go immediately to mps_lib_stdout: see .filter-disable.
|
|
*/
|
|
|
|
|
|
/* Forward declarations */
|
|
|
|
static mps_lib_FILE *filterStream(void);
|
|
static int filterStream_fputc(int c, mps_lib_FILE *stream);
|
|
static int filterStream_fputs(const char *s, mps_lib_FILE *stream);
|
|
static void diag_test(void);
|
|
|
|
|
|
/* Stream -- output to filterStream or to a real mps_lib_FILE stream
|
|
*
|
|
* There are only two functions and two destinations; a full class
|
|
* hierarchy would be overkill! RHSK 2007-08-08.
|
|
*/
|
|
|
|
int Stream_fputc(int c, mps_lib_FILE *stream)
|
|
{
|
|
if(stream == filterStream())
|
|
return filterStream_fputc(c, stream);
|
|
else
|
|
return mps_lib_fputc(c, stream);
|
|
}
|
|
|
|
int Stream_fputs(const char *s, mps_lib_FILE *stream)
|
|
{
|
|
if(stream == filterStream())
|
|
return filterStream_fputs(s, stream);
|
|
else
|
|
return mps_lib_fputs(s, stream);
|
|
}
|
|
|
|
|
|
/* Diag -- a buffer to store a diagnostic
|
|
*
|
|
*/
|
|
|
|
#define DiagSig ((Sig)0x519D1A99) /* SIGnature DIAG */
|
|
|
|
typedef struct DiagStruct {
|
|
Sig sig;
|
|
const char *tag;
|
|
Bool overflow; /* diag > buf? set flag, truncate, force output */
|
|
Count n;
|
|
char buf[DIAG_BUFFER_SIZE];
|
|
} *Diag;
|
|
|
|
static Bool DiagCheck(Diag diag)
|
|
{
|
|
CHECKS(Diag, diag);
|
|
CHECKL(diag->n <= sizeof(diag->buf));
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
/* filterStream -- capable of filtering diagnostics
|
|
*
|
|
* This is not really an mps_lib_FILE*; it is a single global instance
|
|
* of a DiagStruct.
|
|
*
|
|
* Output is stored in a DiagStruct, to be filtered and output
|
|
* (or not) when complete.
|
|
*/
|
|
|
|
static struct DiagStruct filterDiagGlobal = { DiagSig, NULL, FALSE, 0 };
|
|
|
|
static mps_lib_FILE *filterStream(void)
|
|
{
|
|
return (mps_lib_FILE*)&filterDiagGlobal;
|
|
}
|
|
|
|
/* filterStream_under: the underlying stream used to output diags */
|
|
/* that pass the filter. */
|
|
static mps_lib_FILE *filterStream_under(void)
|
|
{
|
|
return mps_lib_stdout;
|
|
}
|
|
|
|
/* .tpmatch: does this rule match current diag's tag and para? */
|
|
enum {
|
|
TPMatch_Unknown = 0, /* initial value = 0 */
|
|
TPMatch_Yes,
|
|
TPMatch_No
|
|
};
|
|
|
|
static void version_diag(void)
|
|
{
|
|
DIAG_SINGLEF(( "MPSVersion",
|
|
"$S", (WriteFS)MPSVersion(), NULL ));
|
|
}
|
|
|
|
static void rules_diag(Rule rules)
|
|
{
|
|
Index ir;
|
|
|
|
AVER(rules);
|
|
DIAG_FIRSTF(( "DiagFilter_Rules",
|
|
"Only showing diags permitted by these tag/paragraph/line"
|
|
" rules:\n", NULL ));
|
|
for(ir = 0; rules[ir].action != NULL; ir++) {
|
|
DIAG_DECL( Rule rule = &rules[ir]; )
|
|
DIAG_MOREF(( "$S$S/$S/$S\n", (WriteFS)rule->action, (WriteFS)rule->tag,
|
|
(WriteFS)rule->para, (WriteFS)rule->line,
|
|
NULL ));
|
|
}
|
|
DIAG_END("DiagFilter_Rules");
|
|
}
|
|
|
|
|
|
/* patternOccurs -- does patt occur in buf[i..j)?
|
|
*
|
|
* Returns true iff patt[0..pattLen) literally occurs in buf[i..j).
|
|
*/
|
|
|
|
static Bool patternOccurs(const char *patt, Count pattLen,
|
|
const char *buf, Index i, Index j)
|
|
{
|
|
Index im; /* start of tentative match */
|
|
Index ip; /* index into patt */
|
|
|
|
AVER(patt);
|
|
AVER(buf);
|
|
AVER(i <= j);
|
|
|
|
/* Search (naively) for patt anywhere inside buf[i..j) */
|
|
for(im = i; im + pattLen <= j; im++) {
|
|
/* Consider upto pattLen chars starting at patt[0] and buf[im] */
|
|
for(ip = 0; ip < pattLen; ip++) {
|
|
if(patt[ip] != buf[im + ip])
|
|
break;
|
|
}
|
|
if(ip == pattLen) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static Bool matchLine(Rule rule, Diag diag, Index i, Index j)
|
|
{
|
|
AVER(rule);
|
|
AVER(diag);
|
|
AVER(i <= j);
|
|
AVER(j <= diag->n);
|
|
|
|
if(rule->line[0] == '*')
|
|
return TRUE;
|
|
|
|
return patternOccurs(rule->line, StringLength(rule->line),
|
|
diag->buf, i, j);
|
|
}
|
|
|
|
static Bool matchPara(Rule rule, Diag diag)
|
|
{
|
|
AVER(rule);
|
|
AVER(diag);
|
|
|
|
if(rule->para[0] == '*')
|
|
return TRUE;
|
|
|
|
return patternOccurs(rule->para, StringLength(rule->para),
|
|
diag->buf, 0, diag->n);
|
|
}
|
|
|
|
static Bool matchTag(Rule rule, const char *tag)
|
|
{
|
|
AVER(rule);
|
|
AVER(rule->tag);
|
|
AVER(tag);
|
|
|
|
if(rule->tag[0] == '*')
|
|
return TRUE;
|
|
|
|
return patternOccurs(rule->tag, StringLength(rule->tag),
|
|
tag, 0, StringLength(tag));
|
|
}
|
|
|
|
static void filterStream_LineOut(Diag diag, Index i, Index j)
|
|
{
|
|
int r;
|
|
|
|
AVER(diag);
|
|
AVER(i <= j);
|
|
AVER(j <= diag->n);
|
|
|
|
r = Stream_fputs(DIAG_PREFIX_LINE, filterStream_under());
|
|
AVER(r != mps_lib_EOF);
|
|
|
|
for(; i < j; i++) {
|
|
char c;
|
|
c = diag->buf[i];
|
|
r = Stream_fputc(c, filterStream_under());
|
|
AVER(r != mps_lib_EOF);
|
|
}
|
|
}
|
|
|
|
|
|
/* filterStream_Output -- output this diag, if the rules select it
|
|
*
|
|
*/
|
|
|
|
static void filterStream_Output(Diag diag, Rule rules)
|
|
{
|
|
static Bool inside = FALSE;
|
|
Res res;
|
|
Count nr;
|
|
Index ir;
|
|
Index i, j;
|
|
Bool nolinesyet = TRUE;
|
|
Bool emptyonce;
|
|
|
|
AVER(!inside);
|
|
inside = TRUE;
|
|
AVER(diag);
|
|
AVER(rules);
|
|
|
|
if(diag->tag == NULL)
|
|
diag->tag = "(no tag)";
|
|
|
|
/* Count the rules */
|
|
for(ir = 0; rules[ir].action != NULL; ir++) {
|
|
rules[ir].tpMatch = TPMatch_Unknown;
|
|
}
|
|
nr = ir;
|
|
|
|
/* Filter */
|
|
/* .empty-diag: Treat a diag that deliberately has no output, */
|
|
/* eg: DIAG_SINGLEF(( "Tag", NULL )), as having a single empty */
|
|
/* 'line'. This is the only time a line may be empty. */
|
|
emptyonce = (diag->n == 0);
|
|
for(i = 0; emptyonce || i < diag->n; i = j) {
|
|
|
|
/* Get the next line [i..j) */
|
|
for(j = i; j < diag->n; j++) {
|
|
if(diag->buf[j] == '\n') {
|
|
j++;
|
|
break;
|
|
}
|
|
}
|
|
AVER(emptyonce || i < j); /* .empty-diag */
|
|
emptyonce = FALSE;
|
|
|
|
/* Find the lowest rule that matches it. */
|
|
ir = nr - 1;
|
|
for(;;) {
|
|
Rule rule = &rules[ir];
|
|
if(rule->tpMatch == TPMatch_Unknown) {
|
|
/* memoize .tpMatch */
|
|
if(matchTag(rule, diag->tag) && matchPara(rule, diag)) {
|
|
rule->tpMatch = TPMatch_Yes;
|
|
} else {
|
|
rule->tpMatch = TPMatch_No;
|
|
}
|
|
}
|
|
if(rule->tpMatch == TPMatch_Yes && matchLine(rule, diag, i, j))
|
|
break;
|
|
AVER(ir != 0); /* there must ALWAYS be a matching rule */
|
|
ir--;
|
|
}
|
|
|
|
/* Do the rule's action. */
|
|
if(0) {
|
|
/* .rules.debug: Turn this on to show which rule applied. */
|
|
Rule rule = &rules[ir];
|
|
(void) WriteF(filterStream_under(), "[$U/$U:", ir, nr,
|
|
" $S$S/$S/$S] ", rule->action, rule->tag,
|
|
rule->para, rule->line, NULL);
|
|
}
|
|
if(rules[ir].action[0] == '+' || diag->overflow) {
|
|
if(nolinesyet) {
|
|
res = WriteF(filterStream_under(),
|
|
DIAG_PREFIX_TAGSTART "$S {", (WriteFS)diag->tag, NULL);
|
|
AVER(res == ResOK);
|
|
nolinesyet = FALSE;
|
|
}
|
|
filterStream_LineOut(diag, i, j);
|
|
}
|
|
}
|
|
|
|
if(diag->overflow) {
|
|
res = WriteF(filterStream_under(),
|
|
"\n--- diagnostic too large: "
|
|
"forced to output, but truncated here ---\n"
|
|
"--- (for a bigger buffer, change DIAG_BUFFER_SIZE) ---\n", NULL);
|
|
AVER(res == ResOK);
|
|
}
|
|
if(!nolinesyet) {
|
|
res = WriteF(filterStream_under(), DIAG_PREFIX_TAGEND "}\n", NULL);
|
|
AVER(res == ResOK);
|
|
}
|
|
inside = FALSE;
|
|
}
|
|
|
|
static void filterStream_TagBegin(mps_lib_FILE *stream, const char *tag)
|
|
{
|
|
static Bool first = TRUE;
|
|
Diag diag;
|
|
|
|
AVER(stream);
|
|
AVER(tag);
|
|
|
|
diag = (Diag)stream;
|
|
AVERT(Diag, diag);
|
|
|
|
if(first) {
|
|
first = FALSE;
|
|
version_diag();
|
|
rules_diag(&RulesGlobal[0]);
|
|
diag_test();
|
|
}
|
|
|
|
if(diag->tag != NULL) {
|
|
/* Be helpful to the poor programmer! */
|
|
(void) WriteF(filterStream_under(),
|
|
"\nWARNING: diag tag \"$S\" is still current"
|
|
" (missing DIAG_END()).",
|
|
diag->tag, NULL);
|
|
}
|
|
AVER(diag->tag == NULL);
|
|
|
|
/* @@ when all diags are tagged, the buffer must be empty */
|
|
/* @@ but for now, as a courtesy... */
|
|
if(diag->n > 0) {
|
|
filterStream_Output(diag, &RulesGlobal[0]);
|
|
diag->n = 0;
|
|
}
|
|
|
|
diag->tag = tag;
|
|
diag->overflow = FALSE;
|
|
AVER(diag->n == 0);
|
|
}
|
|
|
|
static void filterStream_TagEnd(mps_lib_FILE *stream, const char *tag)
|
|
{
|
|
Diag diag;
|
|
diag = (Diag)stream;
|
|
AVERT(Diag, diag);
|
|
|
|
AVER(diag->tag != NULL);
|
|
|
|
if(!StringEqual(diag->tag, tag)) {
|
|
/* Be helpful to the poor programmer! */
|
|
(void) WriteF(filterStream_under(),
|
|
"\nWARNING: diag tag \"$S\" is current, "
|
|
"but got DIAG_END(\"$S\"). (They must match).",
|
|
(WriteFS)diag->tag, (WriteFS)tag, NULL);
|
|
}
|
|
AVER(StringEqual(diag->tag, tag));
|
|
|
|
/* Output the diag */
|
|
filterStream_Output(diag, &RulesGlobal[0]);
|
|
|
|
diag->tag = NULL;
|
|
diag->n = 0;
|
|
}
|
|
|
|
static int filterStream_fputc(int c, mps_lib_FILE *stream)
|
|
{
|
|
Diag diag;
|
|
|
|
AVER(c != mps_lib_EOF);
|
|
AVER(stream == filterStream());
|
|
|
|
diag = (Diag)stream;
|
|
AVERT(Diag, diag);
|
|
/* @@ when all diags are tagged: AVER(diag->tag != NULL); */
|
|
|
|
/* AVER(diag->n + 1 <= sizeof(diag->buf)); */
|
|
if(!(diag->n + 1 <= sizeof(diag->buf))) {
|
|
diag->overflow = TRUE;
|
|
/* ignore failure; do not return mps_lib_EOF */
|
|
return c;
|
|
}
|
|
|
|
/* add c to buffer */
|
|
diag->buf[diag->n++] = (char)c;
|
|
return c;
|
|
}
|
|
|
|
static int filterStream_fputs(const char *s, mps_lib_FILE *stream)
|
|
{
|
|
Diag diag;
|
|
Count l;
|
|
Index i;
|
|
|
|
AVER(s);
|
|
AVER(stream == filterStream());
|
|
|
|
diag = (Diag)stream;
|
|
AVERT(Diag, diag);
|
|
/* @@ when all diags are tagged: AVER(diag->tag != NULL); */
|
|
|
|
l = StringLength(s);
|
|
|
|
/* AVER(diag->n + l <= sizeof(diag->buf)); */
|
|
if(!(diag->n + l <= sizeof(diag->buf))) {
|
|
diag->overflow = TRUE;
|
|
/* ignore failure; do not return mps_lib_EOF */
|
|
return 1;
|
|
}
|
|
|
|
/* add s to buffer */
|
|
for (i = 0; i < l; i++) {
|
|
diag->buf[diag->n++] = s[i];
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* DIAG_WITH_STREAM_AND_WRITEF -- Diagnostic output channel
|
|
*
|
|
* Only used for DIAG_WITH_STREAM_AND_WRITEF; see config.h.
|
|
*/
|
|
|
|
Bool DiagEnabledGlobal = TRUE;
|
|
|
|
Bool DiagIsOn(void)
|
|
{
|
|
return DiagEnabledGlobal;
|
|
}
|
|
|
|
mps_lib_FILE *DiagStream(void)
|
|
{
|
|
/* .filter-disable: the entire filtering mechanism can be turned */
|
|
/* off, so that diagnostics go immediately to mps_lib_stdout, */
|
|
/* with no buffering or filtering. */
|
|
Bool filter = TRUE;
|
|
|
|
if(filter) {
|
|
return filterStream();
|
|
} else {
|
|
return mps_lib_stdout;
|
|
}
|
|
}
|
|
|
|
static void diagTagBegin(mps_lib_FILE *stream, const char *tag)
|
|
{
|
|
AVER(stream);
|
|
AVER(tag);
|
|
|
|
if(stream == filterStream()) {
|
|
filterStream_TagBegin(stream, tag);
|
|
} else {
|
|
Res res;
|
|
res = WriteF(stream, DIAG_PREFIX_TAGSTART "$S {\n", (WriteFS)tag, NULL);
|
|
AVER(res == ResOK);
|
|
}
|
|
}
|
|
|
|
static void diagTagEnd(mps_lib_FILE *stream, const char *tag)
|
|
{
|
|
AVER(stream);
|
|
AVER(tag);
|
|
|
|
if(stream == filterStream()) {
|
|
filterStream_TagEnd(stream, tag);
|
|
} else {
|
|
Res res;
|
|
res = WriteF(stream, DIAG_PREFIX_TAGEND "}\n", tag, NULL);
|
|
AVER(res == ResOK);
|
|
}
|
|
}
|
|
|
|
|
|
/* Diag*F functions -- interface for general MPS code (via macros)
|
|
*
|
|
* These function manage TagBegin/End, and WriteF the text to
|
|
* DiagStream().
|
|
*
|
|
* Direct writing to DiagStream() is also permitted (eg. from a
|
|
* Describe method).
|
|
*/
|
|
|
|
void DiagSingleF(const char *tag, ...)
|
|
{
|
|
va_list args;
|
|
Res res;
|
|
|
|
diagTagBegin(DiagStream(), tag);
|
|
|
|
va_start(args, tag);
|
|
res = WriteF_v(DiagStream(), args);
|
|
AVER(res == ResOK);
|
|
va_end(args);
|
|
|
|
diagTagEnd(DiagStream(), tag);
|
|
}
|
|
|
|
void DiagFirstF(const char *tag, ...)
|
|
{
|
|
va_list args;
|
|
Res res;
|
|
|
|
diagTagBegin(DiagStream(), tag);
|
|
|
|
va_start(args, tag);
|
|
res = WriteF_v(DiagStream(), args);
|
|
AVER(res == ResOK);
|
|
va_end(args);
|
|
}
|
|
|
|
void DiagMoreF(const char *firstformat, ...)
|
|
{
|
|
va_list args;
|
|
Res res;
|
|
|
|
/* ISO C says there must be at least one named parameter: hence */
|
|
/* the named firstformat. It only looks different: there is no */
|
|
/* change from the expected WriteF protocol. (In particular, */
|
|
/* firstformat may legally be NULL, with the variable part empty). */
|
|
|
|
va_start(args, firstformat);
|
|
res = WriteF_firstformat_v(DiagStream(), firstformat, args);
|
|
AVER(res == ResOK);
|
|
va_end(args);
|
|
}
|
|
|
|
void DiagEnd(const char *tag)
|
|
{
|
|
diagTagEnd(DiagStream(), tag);
|
|
}
|
|
|
|
|
|
/* Test Code -- unit tests for this source file
|
|
*
|
|
* These are for developers to run if they modify this source file.
|
|
* There's no point running them otherwise. RHSK.
|
|
*/
|
|
|
|
static void patternOccurs_test(Bool expect, const char *patt,
|
|
const char *text)
|
|
{
|
|
Count pattLen = StringLength(patt);
|
|
Count textLen = StringLength(text);
|
|
enum {bufLen = 100};
|
|
char buf[bufLen];
|
|
Index start, i;
|
|
Count padLen;
|
|
Bool occurs;
|
|
|
|
/* Call patternOccurs with this patt and text 3 times: each time */
|
|
/* putting the text in the buffer at a different offset, to */
|
|
/* verify that patternOccurs is not accepting matches outside the */
|
|
/* [i..j) portion of the buffer. */
|
|
|
|
for(start = 0; start < 21; start += 7) {
|
|
AVER(bufLen > (start + textLen));
|
|
/* put text into buf at start */
|
|
for(i = 0; i < start; i++) {
|
|
buf[i] = 'X';
|
|
}
|
|
for(i = 0; i < textLen; i++) {
|
|
(buf+start)[i] = text[i];
|
|
}
|
|
padLen = bufLen - (start + textLen);
|
|
for(i = 0; i < padLen; i++) {
|
|
(buf+start+textLen)[i] = 'X';
|
|
}
|
|
occurs = patternOccurs(patt, pattLen, buf, start, start+textLen);
|
|
AVER(occurs == expect);
|
|
}
|
|
}
|
|
|
|
static void diag_test(void)
|
|
{
|
|
DIAG_SINGLEF(( "DIAGTEST_Tag1", "text $U.\n", (WriteFU)42, NULL ));
|
|
|
|
DIAG_SINGLEF(( "DIAGTEST_EmptyDiag", NULL ));
|
|
|
|
DIAG_FIRSTF((
|
|
"DIAGTEST_StringEqual",
|
|
"Fred = Fred: $U.\n",
|
|
StringEqual("Fred", "Fred"),
|
|
NULL
|
|
));
|
|
DIAG_MOREF(("Fred = Tom: $U.\n", (WriteFU)StringEqual("Fred", "Tom"), NULL));
|
|
DIAG_MOREF(("Tom = Fred: $U.\n", (WriteFU)StringEqual("Tom", "Fred"), NULL));
|
|
DIAG_MOREF(("0 = Fred: $U.\n", (WriteFU)StringEqual("", "Fred"), NULL));
|
|
DIAG_MOREF(("Fred = 0: $U.\n", (WriteFU)StringEqual("Fred", ""), NULL));
|
|
DIAG_MOREF(("0 = 0: $U.\n", (WriteFU)StringEqual("", ""), NULL));
|
|
DIAG_MOREF(("0 = 000: $U.\n", (WriteFU)StringEqual("", "\0\0"), NULL));
|
|
DIAG_END("DIAGTEST_StringEqual");
|
|
|
|
DIAG_FIRSTF(( "DIAGTEST_patternOccurs", NULL ));
|
|
patternOccurs_test(TRUE, "Fred", "Fred");
|
|
patternOccurs_test(TRUE, "Fred", "XFredX");
|
|
patternOccurs_test(TRUE, "Fred", "FFred");
|
|
patternOccurs_test(TRUE, "Fred", "FrFred");
|
|
patternOccurs_test(TRUE, "Fred", "FreFred");
|
|
patternOccurs_test(TRUE, "Fred", "FreFreFFred");
|
|
patternOccurs_test(TRUE, "Fred", "FredFred");
|
|
patternOccurs_test(TRUE, "Fred", "FFredFre");
|
|
patternOccurs_test(TRUE, "Fred", "FrFredFr");
|
|
patternOccurs_test(TRUE, "Fred", "FreFredF");
|
|
patternOccurs_test(TRUE, "Fred", "FreFreFFredFre");
|
|
patternOccurs_test(TRUE, "Fred", "FredFredF");
|
|
patternOccurs_test(TRUE, "X", "X");
|
|
patternOccurs_test(TRUE, "", "X");
|
|
patternOccurs_test(TRUE, "", "Whatever");
|
|
patternOccurs_test(FALSE, "Fred", "Tom");
|
|
patternOccurs_test(FALSE, "X", "Tom");
|
|
patternOccurs_test(FALSE, "X", "x");
|
|
patternOccurs_test(FALSE, "X", "");
|
|
patternOccurs_test(FALSE, "Whatever", "");
|
|
patternOccurs_test(FALSE, "Fred", "Fre");
|
|
patternOccurs_test(FALSE, "Fred", "red");
|
|
patternOccurs_test(FALSE, "Fred", "Fxred");
|
|
patternOccurs_test(FALSE, "Fred", "Frexd");
|
|
DIAG_END("DIAGTEST_patternOccurs");
|
|
|
|
#if 0
|
|
DIAG_FIRSTF(( "TestTag2", "text $U.\n", (WriteFU)42, NULL ));
|
|
DIAG_MOREF(( NULL ));
|
|
DIAG_MOREF(( "string $S.\n", (WriteFS)"fooey!", NULL ));
|
|
DIAG_MOREF(( NULL ));
|
|
DIAG_MOREF(( "Another string $S.\n", (WriteFS)"baloney!", NULL ));
|
|
DIAG_END( "TestTag2" );
|
|
#endif
|
|
}
|
|
|
|
#endif /* DIAG_WITH_STREAM_AND_WRITEF */
|
|
|
|
|
|
/* C. COPYRIGHT AND LICENSE
|
|
*
|
|
* Copyright (C) 2007 Ravenbrook Limited <http://www.ravenbrook.com/>.
|
|
* 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:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. 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.
|
|
*
|
|
* 3. Redistributions in any form must be accompanied by information on how
|
|
* to obtain complete source code for 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.
|
|
*/
|