Bug 1225203 - Make backtracking register allocator spew / dump output threadsafe r=bhackett

This commit is contained in:
Jon Coppeard 2015-12-18 12:12:02 +00:00
parent f0cf897a7b
commit e0a0fc4ef4
7 changed files with 142 additions and 169 deletions

View File

@ -1229,7 +1229,7 @@ BacktrackingAllocator::processBundle(LiveBundle* bundle)
{
if (JitSpewEnabled(JitSpew_RegAlloc)) {
JitSpew(JitSpew_RegAlloc, "Allocating %s [priority %lu] [weight %lu]",
bundle->toString(), computePriority(bundle), computeSpillWeight(bundle));
bundle->toString().get(), computePriority(bundle), computeSpillWeight(bundle));
}
// A bundle can be processed by doing any of the following:
@ -1322,7 +1322,7 @@ BacktrackingAllocator::computeRequirement(LiveBundle* bundle,
if (policy == LDefinition::FIXED) {
// Fixed policies get a FIXED requirement.
JitSpew(JitSpew_RegAlloc, " Requirement %s, fixed by definition",
reg.def()->output()->toString());
reg.def()->output()->toString().get());
if (!requirement->merge(Requirement(*reg.def()->output())))
return false;
} else if (reg.ins()->isPhi()) {
@ -1398,7 +1398,7 @@ BacktrackingAllocator::tryAllocateRegister(PhysicalRegister& r, LiveBundle* bund
return false;
} else {
JitSpew(JitSpew_RegAlloc, " %s collides with fixed use %s",
rAlias.reg.name(), existing->toString());
rAlias.reg.name(), existing->toString().get());
*pfixed = true;
return true;
}
@ -1417,13 +1417,13 @@ BacktrackingAllocator::tryAllocateRegister(PhysicalRegister& r, LiveBundle* bund
if (aliasedConflicting.length() == 1) {
LiveBundle* existing = aliasedConflicting[0];
JitSpew(JitSpew_RegAlloc, " %s collides with %s [weight %lu]",
r.reg.name(), existing->toString(), computeSpillWeight(existing));
r.reg.name(), existing->toString().get(), computeSpillWeight(existing));
} else {
JitSpew(JitSpew_RegAlloc, " %s collides with the following", r.reg.name());
for (size_t i = 0; i < aliasedConflicting.length(); i++) {
LiveBundle* existing = aliasedConflicting[i];
JitSpew(JitSpew_RegAlloc, " %s [weight %lu]",
existing->toString(), computeSpillWeight(existing));
existing->toString().get(), computeSpillWeight(existing));
}
}
}
@ -1460,7 +1460,7 @@ BacktrackingAllocator::evictBundle(LiveBundle* bundle)
{
if (JitSpewEnabled(JitSpew_RegAlloc)) {
JitSpew(JitSpew_RegAlloc, " Evicting %s [priority %lu] [weight %lu]",
bundle->toString(), computePriority(bundle), computeSpillWeight(bundle));
bundle->toString().get(), computePriority(bundle), computeSpillWeight(bundle));
}
AnyRegister reg(bundle->allocation().toRegister());
@ -1483,9 +1483,9 @@ BacktrackingAllocator::splitAndRequeueBundles(LiveBundle* bundle,
const LiveBundleVector& newBundles)
{
if (JitSpewEnabled(JitSpew_RegAlloc)) {
JitSpew(JitSpew_RegAlloc, " splitting bundle %s into:", bundle->toString());
JitSpew(JitSpew_RegAlloc, " splitting bundle %s into:", bundle->toString().get());
for (size_t i = 0; i < newBundles.length(); i++)
JitSpew(JitSpew_RegAlloc, " %s", newBundles[i]->toString());
JitSpew(JitSpew_RegAlloc, " %s", newBundles[i]->toString().get());
}
// Remove all ranges in the old bundle from their register's list.
@ -2220,74 +2220,55 @@ BacktrackingAllocator::annotateMoveGroups()
// Debugging methods
/////////////////////////////////////////////////////////////////////
#ifdef DEBUG
#ifdef JS_JITSPEW
const char*
UniqueChars
LiveRange::toString() const
{
// Not reentrant!
static char buf[10000];
AutoEnterOOMUnsafeRegion oomUnsafe;
char* cursor = buf;
char* end = cursor + sizeof(buf);
char* buf = JS_smprintf("v%u [%u,%u)", hasVreg() ? vreg() : 0, from().bits(), to().bits());
uint32_t n = JS_snprintf(cursor, end - cursor, "v%u [%u,%u)",
hasVreg() ? vreg() : 0, from().bits(), to().bits());
if (n >= uint32_t(end - cursor))
return "(truncated)";
cursor += n;
if (buf && bundle() && !bundle()->allocation().isBogus())
buf = JS_sprintf_append(buf, " %s", bundle()->allocation().toString().get());
if (bundle() && !bundle()->allocation().isBogus()) {
n = JS_snprintf(cursor, end - cursor, " %s", bundle()->allocation().toString());
if (n >= uint32_t(end - cursor))
return " (truncated)";
cursor += n;
}
if (buf && hasDefinition())
buf = JS_sprintf_append(buf, " (def)");
if (hasDefinition()) {
n = JS_snprintf(cursor, end - cursor, " (def)");
if (n >= uint32_t(end - cursor))
return " (truncated)";
cursor += n;
}
for (UsePositionIterator iter = usesBegin(); buf && iter; iter++)
buf = JS_sprintf_append(buf, " %s@%u", iter->use()->toString().get(), iter->pos.bits());
for (UsePositionIterator iter = usesBegin(); iter; iter++) {
n = JS_snprintf(cursor, end - cursor, " %s@%u", iter->use()->toString(), iter->pos.bits());
if (n >= uint32_t(end - cursor))
return " (truncated)";
cursor += n;
}
if (!buf)
oomUnsafe.crash("LiveRange::toString()");
return buf;
return UniqueChars(buf);
}
const char*
UniqueChars
LiveBundle::toString() const
{
// Not reentrant!
static char buf[10000];
AutoEnterOOMUnsafeRegion oomUnsafe;
char* cursor = buf;
char* end = cursor + sizeof(buf);
char *buf = JS_smprintf("");
for (LiveRange::BundleLinkIterator iter = rangesBegin(); iter; iter++) {
uint32_t n = JS_snprintf(cursor, end - cursor, "%s %s",
(iter == rangesBegin()) ? "" : " ##",
LiveRange::get(*iter)->toString());
if (n >= uint32_t(end - cursor))
return "(truncated)";
cursor += n;
for (LiveRange::BundleLinkIterator iter = rangesBegin(); buf && iter; iter++) {
buf = JS_smprintf(buf, "%s %s",
(iter == rangesBegin()) ? "" : " ##",
LiveRange::get(*iter)->toString().get());
}
return buf;
if (!buf)
oomUnsafe.crash("LiveBundle::toString()");
return UniqueChars(buf);
}
#endif // DEBUG
#endif // JS_JITSPEW
void
BacktrackingAllocator::dumpVregs()
{
#ifdef DEBUG
#ifdef JS_JITSPEW
MOZ_ASSERT(!vregs[0u].hasRanges());
fprintf(stderr, "Live ranges by virtual register:\n");
@ -2298,7 +2279,7 @@ BacktrackingAllocator::dumpVregs()
for (LiveRange::RegisterLinkIterator iter = reg.rangesBegin(); iter; iter++) {
if (iter != reg.rangesBegin())
fprintf(stderr, " ## ");
fprintf(stderr, "%s", LiveRange::get(*iter)->toString());
fprintf(stderr, "%s", LiveRange::get(*iter)->toString().get());
}
fprintf(stderr, "\n");
}
@ -2315,7 +2296,7 @@ BacktrackingAllocator::dumpVregs()
for (LiveRange::BundleLinkIterator iter = bundle->rangesBegin(); iter; iter++) {
if (iter != bundle->rangesBegin())
fprintf(stderr, " ## ");
fprintf(stderr, "%s", LiveRange::get(*iter)->toString());
fprintf(stderr, "%s", LiveRange::get(*iter)->toString().get());
}
fprintf(stderr, "\n");
}
@ -2327,12 +2308,12 @@ BacktrackingAllocator::dumpVregs()
void
BacktrackingAllocator::dumpFixedRanges()
{
#ifdef DEBUG
fprintf(stderr, "Live ranges by physical register: %s\n", callRanges->toString());
#endif // DEBUG
#ifdef JS_JITSPEW
fprintf(stderr, "Live ranges by physical register: %s\n", callRanges->toString().get());
#endif // JS_JITSPEW
}
#ifdef DEBUG
#ifdef JS_JITSPEW
struct BacktrackingAllocator::PrintLiveRange
{
bool& first_;
@ -2345,7 +2326,7 @@ struct BacktrackingAllocator::PrintLiveRange
first_ = false;
else
fprintf(stderr, " /");
fprintf(stderr, " %s", range->toString());
fprintf(stderr, " %s", range->toString().get());
}
};
#endif
@ -2353,7 +2334,7 @@ struct BacktrackingAllocator::PrintLiveRange
void
BacktrackingAllocator::dumpAllocations()
{
#ifdef DEBUG
#ifdef JS_JITSPEW
fprintf(stderr, "Allocations:\n");
dumpVregs();
@ -2370,7 +2351,7 @@ BacktrackingAllocator::dumpAllocations()
}
fprintf(stderr, "\n");
#endif // DEBUG
#endif // JS_JITSPEW
}
///////////////////////////////////////////////////////////////////////////////
@ -2569,7 +2550,7 @@ BacktrackingAllocator::trySplitAcrossHotcode(LiveBundle* bundle, bool* success)
return true;
}
JitSpew(JitSpew_RegAlloc, " split across hot range %s", hotRange->toString());
JitSpew(JitSpew_RegAlloc, " split across hot range %s", hotRange->toString().get());
// Tweak the splitting method when compiling asm.js code to look at actual
// uses within the hot/cold code. This heuristic is in place as the below

View File

@ -344,11 +344,9 @@ class LiveRange : public TempObject
hasDefinition_ = true;
}
// Return a string describing this range. This is not re-entrant!
#ifdef DEBUG
const char* toString() const;
#else
const char* toString() const { return "???"; }
#ifdef JS_JITSPEW
// Return a string describing this range.
UniqueChars toString() const;
#endif
// Comparator for use in range splay trees.
@ -467,11 +465,9 @@ class LiveBundle : public TempObject
return spillParent_;
}
// Return a string describing this bundle. This is not re-entrant!
#ifdef DEBUG
const char* toString() const;
#else
const char* toString() const { return "???"; }
#ifdef JS_JITSPEW
// Return a string describing this bundle.
UniqueChars toString() const;
#endif
};

View File

@ -97,7 +97,7 @@ C1Spewer::spewRanges(GenericPrinter& out, BacktrackingAllocator* regalloc, LNode
for (LiveRange::RegisterLinkIterator iter = vreg->rangesBegin(); iter; iter++) {
LiveRange* range = LiveRange::get(*iter);
out.printf("%d object \"", id);
out.printf("%s", range->bundle()->allocation().toString());
out.printf("%s", range->bundle()->allocation().toString().get());
out.printf("\" %d -1", id);
out.printf(" [%u, %u[", range->from().bits(), range->to().bits());
for (UsePositionIterator usePos(range->usesBegin()); usePos; usePos++)

View File

@ -371,7 +371,7 @@ JSONSpewer::spewRanges(BacktrackingAllocator* regalloc)
beginObject();
property("allocation");
out_.printf("\"%s\"", range->bundle()->allocation().toString());
out_.printf("\"%s\"", range->bundle()->allocation().toString().get());
integerProperty("start", range->from().bits());
integerProperty("end", range->to().bits());
endObject();

View File

@ -358,109 +358,107 @@ static const char * const TypeChars[] =
#endif
};
static void
PrintDefinition(char* buf, size_t size, const LDefinition& def)
{
char* cursor = buf;
char* end = buf + size;
cursor += JS_snprintf(cursor, end - cursor, "v%u", def.virtualRegister());
cursor += JS_snprintf(cursor, end - cursor, "<%s>", TypeChars[def.type()]);
if (def.policy() == LDefinition::FIXED)
cursor += JS_snprintf(cursor, end - cursor, ":%s", def.output()->toString());
else if (def.policy() == LDefinition::MUST_REUSE_INPUT)
cursor += JS_snprintf(cursor, end - cursor, ":tied(%u)", def.getReusedInput());
}
const char*
UniqueChars
LDefinition::toString() const
{
// Not reentrant!
static char buf[40];
AutoEnterOOMUnsafeRegion oomUnsafe;
if (isBogusTemp())
return "bogus";
char* buf;
if (isBogusTemp()) {
buf = JS_smprintf("bogus");
} else {
buf = JS_smprintf("v%u<%s>", virtualRegister(), TypeChars[type()]);
if (buf) {
if (policy() == LDefinition::FIXED)
buf = JS_sprintf_append(buf, ":%s", output()->toString().get());
else if (policy() == LDefinition::MUST_REUSE_INPUT)
buf = JS_sprintf_append(buf, ":tied(%u)", getReusedInput());
}
}
PrintDefinition(buf, sizeof(buf), *this);
return buf;
if (!buf)
oomUnsafe.crash("LDefinition::toString()");
return UniqueChars(buf);
}
static void
PrintUse(char* buf, size_t size, const LUse* use)
static char*
PrintUse(const LUse* use)
{
switch (use->policy()) {
case LUse::REGISTER:
JS_snprintf(buf, size, "v%d:r", use->virtualRegister());
break;
return JS_smprintf("v%d:r", use->virtualRegister());
case LUse::FIXED:
JS_snprintf(buf, size, "v%d:%s", use->virtualRegister(),
AnyRegister::FromCode(use->registerCode()).name());
break;
return JS_smprintf("v%d:%s", use->virtualRegister(),
AnyRegister::FromCode(use->registerCode()).name());
case LUse::ANY:
JS_snprintf(buf, size, "v%d:r?", use->virtualRegister());
break;
return JS_smprintf("v%d:r?", use->virtualRegister());
case LUse::KEEPALIVE:
JS_snprintf(buf, size, "v%d:*", use->virtualRegister());
break;
return JS_smprintf("v%d:*", use->virtualRegister());
case LUse::RECOVERED_INPUT:
JS_snprintf(buf, size, "v%d:**", use->virtualRegister());
break;
return JS_smprintf("v%d:**", use->virtualRegister());
default:
MOZ_CRASH("invalid use policy");
}
}
const char*
UniqueChars
LAllocation::toString() const
{
// Not reentrant!
static char buf[40];
AutoEnterOOMUnsafeRegion oomUnsafe;
if (isBogus())
return "bogus";
switch (kind()) {
case LAllocation::CONSTANT_VALUE:
case LAllocation::CONSTANT_INDEX:
return "c";
case LAllocation::GPR:
JS_snprintf(buf, sizeof(buf), "%s", toGeneralReg()->reg().name());
return buf;
case LAllocation::FPU:
JS_snprintf(buf, sizeof(buf), "%s", toFloatReg()->reg().name());
return buf;
case LAllocation::STACK_SLOT:
JS_snprintf(buf, sizeof(buf), "stack:%d", toStackSlot()->slot());
return buf;
case LAllocation::ARGUMENT_SLOT:
JS_snprintf(buf, sizeof(buf), "arg:%d", toArgument()->index());
return buf;
case LAllocation::USE:
PrintUse(buf, sizeof(buf), toUse());
return buf;
default:
MOZ_CRASH("what?");
char* buf;
if (isBogus()) {
buf = JS_smprintf("bogus");
} else {
switch (kind()) {
case LAllocation::CONSTANT_VALUE:
case LAllocation::CONSTANT_INDEX:
buf = JS_smprintf("c");
break;
case LAllocation::GPR:
buf = JS_smprintf("%s", toGeneralReg()->reg().name());
break;
case LAllocation::FPU:
buf = JS_smprintf("%s", toFloatReg()->reg().name());
break;
case LAllocation::STACK_SLOT:
buf = JS_smprintf("stack:%d", toStackSlot()->slot());
break;
case LAllocation::ARGUMENT_SLOT:
buf = JS_smprintf("arg:%d", toArgument()->index());
break;
case LAllocation::USE:
buf = PrintUse(toUse());
break;
default:
MOZ_CRASH("what?");
}
}
if (!buf)
oomUnsafe.crash("LAllocation::toString()");
return UniqueChars(buf);
}
void
LAllocation::dump() const
{
fprintf(stderr, "%s\n", toString());
fprintf(stderr, "%s\n", toString().get());
}
void
LDefinition::dump() const
{
fprintf(stderr, "%s\n", toString());
fprintf(stderr, "%s\n", toString().get());
}
void
LNode::printOperands(GenericPrinter& out)
{
for (size_t i = 0, e = numOperands(); i < e; i++) {
out.printf(" (%s)", getOperand(i)->toString());
out.printf(" (%s)", getOperand(i)->toString().get());
if (i != numOperands() - 1)
out.printf(",");
}
@ -490,7 +488,7 @@ LNode::dump(GenericPrinter& out)
if (numDefs() != 0) {
out.printf("{");
for (size_t i = 0; i < numDefs(); i++) {
out.printf("%s", getDef(i)->toString());
out.printf("%s", getDef(i)->toString().get());
if (i != numDefs() - 1)
out.printf(", ");
}
@ -503,7 +501,7 @@ LNode::dump(GenericPrinter& out)
if (numTemps()) {
out.printf(" t=(");
for (size_t i = 0; i < numTemps(); i++) {
out.printf("%s", getTemp(i)->toString());
out.printf("%s", getTemp(i)->toString().get());
if (i != numTemps() - 1)
out.printf(", ");
}
@ -599,9 +597,7 @@ LMoveGroup::printOperands(GenericPrinter& out)
{
for (size_t i = 0; i < numMoves(); i++) {
const LMove& move = getMove(i);
// Use two printfs, as LAllocation::toString is not reentrant.
out.printf(" [%s", move.from().toString());
out.printf(" -> %s", move.to().toString());
out.printf(" [%s -> %s", move.from().toString().get(), move.to().toString().get());
#ifdef DEBUG
out.printf(", %s", TypeChars[move.type()]);
#endif

View File

@ -183,7 +183,7 @@ class LAllocation : public TempObject
return bits_;
}
const char* toString() const;
UniqueChars toString() const;
bool aliases(const LAllocation& other) const;
void dump() const;
@ -577,7 +577,7 @@ class LDefinition
}
}
const char* toString() const;
UniqueChars toString() const;
void dump() const;
};

View File

@ -291,7 +291,7 @@ AllocationIntegrityState::checkSafepointAllocation(LInstruction* ins,
case LDefinition::OBJECT:
if (populateSafepoints) {
JitSpew(JitSpew_RegAlloc, "Safepoint object v%u i%u %s",
vreg, ins->id(), alloc.toString());
vreg, ins->id(), alloc.toString().get());
if (!safepoint->addGcPointer(alloc))
return false;
}
@ -300,7 +300,7 @@ AllocationIntegrityState::checkSafepointAllocation(LInstruction* ins,
case LDefinition::SLOTS:
if (populateSafepoints) {
JitSpew(JitSpew_RegAlloc, "Safepoint slots v%u i%u %s",
vreg, ins->id(), alloc.toString());
vreg, ins->id(), alloc.toString().get());
if (!safepoint->addSlotsOrElementsPointer(alloc))
return false;
}
@ -314,7 +314,7 @@ AllocationIntegrityState::checkSafepointAllocation(LInstruction* ins,
case LDefinition::TYPE:
if (populateSafepoints) {
JitSpew(JitSpew_RegAlloc, "Safepoint type v%u i%u %s",
vreg, ins->id(), alloc.toString());
vreg, ins->id(), alloc.toString().get());
if (!safepoint->addNunboxType(vreg, alloc))
return false;
}
@ -322,7 +322,7 @@ AllocationIntegrityState::checkSafepointAllocation(LInstruction* ins,
case LDefinition::PAYLOAD:
if (populateSafepoints) {
JitSpew(JitSpew_RegAlloc, "Safepoint payload v%u i%u %s",
vreg, ins->id(), alloc.toString());
vreg, ins->id(), alloc.toString().get());
if (!safepoint->addNunboxPayload(vreg, alloc))
return false;
}
@ -332,7 +332,7 @@ AllocationIntegrityState::checkSafepointAllocation(LInstruction* ins,
case LDefinition::BOX:
if (populateSafepoints) {
JitSpew(JitSpew_RegAlloc, "Safepoint boxed value v%u i%u %s",
vreg, ins->id(), alloc.toString());
vreg, ins->id(), alloc.toString().get());
if (!safepoint->addBoxedValue(alloc))
return false;
}
@ -391,9 +391,9 @@ AllocationIntegrityState::dump()
fprintf(stderr, "[%u,%u Phi] [def %s] ",
input.bits(),
output.bits(),
phi->getDef(0)->toString());
phi->getDef(0)->toString().get());
for (size_t j = 0; j < phi->numOperands(); j++)
fprintf(stderr, " [use %s]", info.inputs[j].toString());
fprintf(stderr, " [use %s]", info.inputs[j].toString().get());
fprintf(stderr, "\n");
}
@ -412,29 +412,29 @@ AllocationIntegrityState::dump()
if (ins->isMoveGroup()) {
LMoveGroup* group = ins->toMoveGroup();
for (int i = group->numMoves() - 1; i >= 0; i--) {
// Use two printfs, as LAllocation::toString is not reentrant.
fprintf(stderr, " [%s", group->getMove(i).from().toString());
fprintf(stderr, " -> %s]", group->getMove(i).to().toString());
fprintf(stderr, " [%s -> %s]",
group->getMove(i).from().toString().get(),
group->getMove(i).to().toString().get());
}
fprintf(stderr, "\n");
continue;
}
for (size_t i = 0; i < ins->numDefs(); i++)
fprintf(stderr, " [def %s]", ins->getDef(i)->toString());
fprintf(stderr, " [def %s]", ins->getDef(i)->toString().get());
for (size_t i = 0; i < ins->numTemps(); i++) {
LDefinition* temp = ins->getTemp(i);
if (!temp->isBogusTemp())
fprintf(stderr, " [temp v%u %s]", info.temps[i].virtualRegister(),
temp->toString());
temp->toString().get());
}
size_t index = 0;
for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next()) {
fprintf(stderr, " [use %s", info.inputs[index++].toString());
fprintf(stderr, " [use %s", info.inputs[index++].toString().get());
if (!alloc->isConstant())
fprintf(stderr, " %s", alloc->toString());
fprintf(stderr, " %s", alloc->toString().get());
fprintf(stderr, "]");
}
@ -459,7 +459,7 @@ AllocationIntegrityState::dump()
for (size_t i = 0; i < seenOrdered.length(); i++) {
IntegrityItem item = seenOrdered[i];
fprintf(stderr, " block %u reg v%u alloc %s\n",
item.block->mir()->id(), item.vreg, item.alloc.toString());
item.block->mir()->id(), item.vreg, item.alloc.toString().get());
}
}
@ -535,9 +535,9 @@ RegisterAllocator::dumpInstructions()
fprintf(stderr, "[%u,%u Phi] [def %s]",
inputOf(phi).bits(),
outputOf(phi).bits(),
phi->getDef(0)->toString());
phi->getDef(0)->toString().get());
for (size_t j = 0; j < phi->numOperands(); j++)
fprintf(stderr, " [use %s]", phi->getOperand(j)->toString());
fprintf(stderr, " [use %s]", phi->getOperand(j)->toString().get());
fprintf(stderr, "\n");
}
@ -553,25 +553,25 @@ RegisterAllocator::dumpInstructions()
LMoveGroup* group = ins->toMoveGroup();
for (int i = group->numMoves() - 1; i >= 0; i--) {
// Use two printfs, as LAllocation::toString is not reentant.
fprintf(stderr, " [%s", group->getMove(i).from().toString());
fprintf(stderr, " -> %s]", group->getMove(i).to().toString());
fprintf(stderr, " [%s", group->getMove(i).from().toString().get());
fprintf(stderr, " -> %s]", group->getMove(i).to().toString().get());
}
fprintf(stderr, "\n");
continue;
}
for (size_t i = 0; i < ins->numDefs(); i++)
fprintf(stderr, " [def %s]", ins->getDef(i)->toString());
fprintf(stderr, " [def %s]", ins->getDef(i)->toString().get());
for (size_t i = 0; i < ins->numTemps(); i++) {
LDefinition* temp = ins->getTemp(i);
if (!temp->isBogusTemp())
fprintf(stderr, " [temp %s]", temp->toString());
fprintf(stderr, " [temp %s]", temp->toString().get());
}
for (LInstruction::InputIterator alloc(*ins); alloc.more(); alloc.next()) {
if (!alloc->isBogus())
fprintf(stderr, " [use %s]", alloc->toString());
fprintf(stderr, " [use %s]", alloc->toString().get());
}
fprintf(stderr, "\n");