/* Web Polygraph http://www.web-polygraph.org/
* (C) 2003-2006 The Measurement Factory
* Licensed under the Apache License, Version 2.0 */
#include "pgl/pgl.h"
#include "xstd/Rnd.h"
#include "xstd/gadgets.h"
#include "base/polyVersion.h"
#include "xml/XmlAttr.h"
#include "xml/XmlText.h"
#include "xml/XmlParagraph.h"
#include "xml/XmlTable.h"
#include "xml/XmlSection.h"
#include "pgl/PglPp.h"
#include "pgl/PglCtx.h"
#include "pgl/PglStaticSemx.h"
#include "pgl/PglStringSym.h"
#include "pgl/PglDistrSym.h"
#include "pgl/AgentArrIter.h"
#include "pgl/GoalSym.h"
#include "pgl/PhaseSym.h"
#include "pgl/MimeSym.h"
#include "pgl/ContentSym.h"
#include "pgl/PopDistr.h"
#include "pgl/PopModelSym.h"
#include "pgl/ServerSym.h"
#include "pgl/RobotSym.h"
struct PglParser {}; // stub for future use
static
ostream &addAnd(ostream &os) {
if (os.tellp())
os << ", ";
return os;
}
static
ostream &addAnd(ostream &os, int idx, int count) {
if (!idx)
return os;
if (idx == count-1)
return os << (count == 2 ? " and " : ", and ");
return os << ", ";
}
static
ostream &addOneOrMany(ostream &os, int count, const String &oneStr, const char *manyStr = 0) {
if (count == 1)
return os << oneStr;
if (manyStr)
return os << manyStr;
// XXX: 'h' only if 'sh' and 'zh';
return os << oneStr << (strchr("szjh", oneStr.last()) ? "es" : "s");
}
static
double phaseFactor(const Array<PhaseSym*> &phases, int idx, PhaseSym::FactorFunc fbeg, PhaseSym::FactorFunc fend, bool checkBeg) {
if (idx < 0)
return 1;
const PhaseSym *ph = phases[idx];
const PhaseSym::FactorFunc fcur = checkBeg ? fbeg : fend;
double fact = 1;
if ((ph->*fcur)(fact))
return fact;
if (checkBeg)
return phaseFactor(phases, idx-1, fbeg, fend, false);
else
return phaseFactor(phases, idx, fbeg, fend, true);
}
static
void buildPhaseTable(XmlTable &table, const Array<PhaseSym*> &phases) {
static PhaseSym::FactorFunc funcs[6] = {
&PhaseSym::populusFactorBeg, &PhaseSym::populusFactorEnd,
&PhaseSym::recurFactorBeg, &PhaseSym::recurFactorEnd,
&PhaseSym::specialMsgFactorBeg, &PhaseSym::specialMsgFactorEnd
};
table << XmlAttr::Int("border", 1) << XmlAttr("bgcolor", "#FFFFFF");
XmlTableRec r1, r2, r3;
r1
<< XmlTableHeading("Phase", 1, 3)
<< XmlTableHeading("Factors (%)", 3*2, 1)
<< XmlTableHeading("Other", 1, 3);
r2
<< XmlTableHeading("Populus", 2, 1)
<< XmlTableHeading("Recurrence", 2, 1)
<< XmlTableHeading("Special Msgs", 2, 1);
for (int c = 0; c < 3; ++c) {
r3 << XmlTableHeading("beg") << XmlTableHeading("end");
}
XmlTableHeader header;
header << r1 << r2 << r3;
header << XmlAttr("bgcolor", "#BBBBBB");
table << header;
// we need higher precision if at least one cell needs it
bool highPrec = false;
{for (int i = 0; !highPrec && i < phases.count(); ++i) {
for (int f = 0; !highPrec && f < 6; ++f) {
const double x = 100*(f % 2 ?
phaseFactor(phases, i, funcs[f-1], funcs[f], false) :
phaseFactor(phases, i, funcs[f], funcs[f+1], true));
highPrec = floor(x) < floor(x + 0.999);
}
}}
for (int i = 0; i < phases.count(); ++i) {
XmlTableRec rec;
const PhaseSym &phase = *phases[i];
rec << XmlTableHeading(phase.name());
{for (int f = 0; f < 6; ++f) {
const double x = 100*(f % 2 ?
phaseFactor(phases, i, funcs[f-1], funcs[f], false) :
phaseFactor(phases, i, funcs[f], funcs[f+1], true));
XmlTextTag<XmlTableCell> cell;
cell << XmlAttr("align", "right");
if (highPrec)
cell.buf() << x;
else
cell.buf() << (int)x;
rec << cell;
}}
XmlTextTag<XmlTableCell> cell;
if (phase.rptmstat())
cell.buf() << addAnd << "rptmstat enabled";
bool ls = false;
if (phase.logStats(ls))
cell.buf() << addAnd << (ls ? "" : "do not ") << "log stats";
bool wwf = false;
if (phase.waitWssFreeze(wwf) && addAnd(cell.buf()))
cell.buf() << addAnd << (wwf ? "" : "do not ") << "wait for WSS to freeze";
if (!cell.buf().tellp())
cell.buf() << " ";
rec << cell;
table << rec;
}
}
static
Time testDuration(const Array<PhaseSym*> &phases, int &fixCount, int &varCount) {
fixCount = varCount = 0;
Time dur(0,0);
for (int i = 0; i < phases.count(); ++i) {
if (GoalSym *gs = phases[i]->goal()) {
const Time d = gs->duration();
if (d >= 0) {
fixCount++;
dur += d;
}
}
}
varCount = phases.count() - fixCount;
return dur;
}
static
const char *changeStr(double prev, double cur) {
return prev <= cur ? "increases" : "decreases";
}
static
void explainPhase(XmlTag §, const Array<PhaseSym*> &phases, int idx) {
const PhaseSym &phase = *phases[idx];
XmlTextTag<XmlParagraph> p;
const Time dur = phase.goal() ? phase.goal()->duration() : Time();
if (dur >= 0)
p.buf() << "Phase \"" << phase.name() << "\" lasts for " << dur << ". ";
else
p.buf() << "Phase \"" << phase.name() << "\" does not have a time-based duration configured. ";
double prev, cur;
double d;
prev = phaseFactor(phases, idx, &PhaseSym::populusFactorBeg, &PhaseSym::populusFactorEnd, true);
cur = phaseFactor(phases, idx, &PhaseSym::populusFactorBeg, &PhaseSym::populusFactorEnd, false);
if (!phase.populusFactorEnd(d)) {
p.buf() << "During this phase, the robot population size remains "
<< "stable at " << (cur*100) << "% of its peak level. ";
} else {
p.buf() << "During this phase, the robot population size " << changeStr(prev, cur)
<< " from " << (prev*100) << "% to " << (cur*100) << "%. ";
}
prev = phaseFactor(phases, idx, &PhaseSym::loadFactorBeg, &PhaseSym::loadFactorEnd, true);
cur = phaseFactor(phases, idx, &PhaseSym::loadFactorBeg, &PhaseSym::loadFactorEnd, false);
if (!phase.loadFactorEnd(d)) {
p.buf() << "The offered per-robot load remains "
<< "stable at " << (cur*100) << "% of its peak level. ";
} else {
p.buf() << "The offered per-robot load level " << changeStr(prev, cur)
<< " from " << (prev*100) << "% to " << (cur*100) << "%. ";
}
if (phase.rptmstat()) {
p.buf() << "However, load level is also subject to response time "
<< "constraints since rptmstat controls are enabled. ";
// XXX: report rptmstat config
}
prev = phaseFactor(phases, idx, &PhaseSym::recurFactorBeg, &PhaseSym::recurFactorEnd, true);
cur = phaseFactor(phases, idx, &PhaseSym::recurFactorBeg, &PhaseSym::recurFactorEnd, false);
if (!phase.recurFactorEnd(d)) {
p.buf() << "The recurrence level remains "
<< "stable at " << (cur*100) << "% of robot recurrence ratios. ";
} else {
p.buf() << "The offered recurrence level " << changeStr(prev, cur)
<< " from " << (prev*100) << "% to " << (cur*100)
<< "% of robot recurrence ratios. ";
}
prev = phaseFactor(phases, idx, &PhaseSym::specialMsgFactorBeg, &PhaseSym::specialMsgFactorEnd, true);
cur = phaseFactor(phases, idx, &PhaseSym::specialMsgFactorBeg, &PhaseSym::specialMsgFactorEnd, false);
if (!phase.specialMsgFactorEnd(d)) {
p.buf() << "The portion of special messages remains "
<< "stable at " << (cur*100) << "%. ";
} else {
p.buf() << "The portion of special messages changes " << changeStr(prev, cur)
<< " from " << (prev*100) << "% to " << (cur*100) << "%. ";
}
Array<const StatsSampleSym*> samples;
if (phase.statsSamples(samples)) {
p.buf() << samples.count() << " samples of per-transaction"
<< " statistics are collected. ";
}
bool doIt;
if (phase.logStats(doIt) && !doIt) {
p.buf() << "No phase statistics is logged for this phase. ";
}
if (phase.waitWssFreeze(doIt) && doIt) {
p.buf() << "The phase will continue until working set size is frozen. ";
}
sect << p;
}
static
void explainPhases(XmlNodes &code, const PglParser &) {
const Array<PhaseSym*> &phases = PglStaticSemx::TheSchedule;
XmlSection sect(2, "Phase schedule");
if (phases.count()) {
XmlTextTag<XmlParagraph> p;
p.buf() << "The workload schedule consists of " << phases.count();
addOneOrMany(p.buf(), phases.count(), " phase") << ". ";
int fixDurCount = 0;
int varDurCount = 0;
const Time fixedDur = testDuration(phases, fixDurCount, varDurCount);
p.buf() << "The schedule includes " << fixDurCount << " phases "
<< "with time-based goals";
if (varDurCount)
addOneOrMany(p.buf() << " and " << varDurCount << " other ", varDurCount, "phase");
p.buf() << ". ";
if (fixedDur > 0) {
p.buf() << "The total test duration (based on the"
<< " time-based goals) is about " << fixedDur << ". ";
}
sect << p;
XmlTag c("center");
XmlTable table;
buildPhaseTable(table, phases);
c << table;
sect << c;
for (int i = 0; i < phases.count(); ++i) {
explainPhase(sect, phases, i);
}
} else {
XmlTextTag<XmlParagraph> p;
p.buf() << "The workload does not define a schedule. "
<< "Polygraph will use a default schedule consisting of a "
<< "single phase with no goal and default factors. ";
sect << p;
}
code << sect;
}
static
void explainAgent(XmlNodes &code, const AgentSym &agent, const String &type) {
XmlText t;
if (const RndDistr *ul = agent.pconnUseLmt()) {
t.buf() << "This " << type << " uses persistent connections. "
<< "The number of transactions per connection is distributed as ";
ul->print(t.buf(), &DistrSym::IntArgPrinter) << ". ";
const Time tout = agent.idlePconnTimeout();
if (tout >= 0) {
t.buf() << "Idle persistent connections are closed after a "
<< tout << " timeout. ";
} else {
t.buf() << "Idle persistent connections are never closed by this "
<< type << ". ";
}
} else {
t.buf() << "This " << type << " does not use persistent connections. ";
}
code << t;
}
static
void explainMsgTypes(XmlNodes &code, const AgentSym &agent, const String &msgKind) {
XmlText t;
Array<StringSym*> msgTypes;
Array<double> tprobs;
if (agent.msgTypes(msgTypes, tprobs)) {
t.buf() << "The following " << msgTypes.count() << ' ' << msgKind << ' ';
addOneOrMany(t.buf(), msgTypes.count(), "type is", "types are")
<< " used: ";
for (int i = 0; i < msgTypes.count(); ++i) {
addAnd(t.buf(), i, msgTypes.count());
t.buf() << '"' << msgTypes[i]->val() << '"'
<< " (" << (100*tprobs[i]) << "%";
if (!i)
t.buf() << " of all possible " << msgKind << " types";
t.buf() << ")";
}
t.buf() << ". ";
} else {
t.buf() << "Only basic " << msgKind << " types are used. ";
}
code << t;
}
static
void explainServer(XmlTag §, const ServerSym &srv) {
XmlTextTag<XmlParagraph> p;
Array<ContentSym*> ccfgs;
Array<double> cprobs;
if (srv.contents(ccfgs, cprobs)) {
p.buf() << "Server \"" << srv.kind() << "\" hosts the following "
<< ccfgs.count() << " content ";
addOneOrMany(p.buf(), ccfgs.count(), "type") << ": ";
for (int i = 0; i < ccfgs.count(); ++i) {
addAnd(p.buf(), i, ccfgs.count());
p.buf() << '"' << ccfgs[i]->kind() << '"'
<< " (" << (100*cprobs[i]) << "%";
if (!i)
p.buf() << " of all hosted content";
p.buf() << ")";
}
p.buf() << ". ";
} else {
p.buf() << "Server type \"" << srv.kind() << "\" does not host "
" any content!";
}
Array<ContentSym*> dacfgs;
Array<double> daprobs;
if (srv.directAccess(dacfgs, daprobs)) {
p.buf() << "The following "
<< dacfgs.count() << " content ";
addOneOrMany(p.buf(), dacfgs.count(), "type") <<
" can be accessed directly: ";
for (int i = 0; i < dacfgs.count(); ++i) {
addAnd(p.buf(), i, dacfgs.count());
p.buf() << '"' << dacfgs[i]->kind() << '"';
}
p.buf() << ". ";
} else {
p.buf() << "No hosted content can be accessed direclty. ";
}
if (RndDistr *xt = srv.xactThink()) {
p.buf() << "Server \"think time\" distribution is set to ";
xt->print(p.buf(), &DistrSym::TimeArgPrinter) << ". ";
} else {
p.buf() << "The server responds to all requests without"
<< " artificial delays. ";
}
XmlNodes code;
explainAgent(code, srv, "server");
explainMsgTypes(code, srv, "reply");
p << code;
sect << p;
}
static
void explainServers(XmlNodes &code, const PglParser &) {
XmlSection sect(2, "Servers configuration");
Array<const ServerSym *> servers;
for (AgentArrIter i(PglStaticSemx::TheAgentsToUse, 0, "Server"); i; i.nextSym())
servers.append(&(ServerSym&)i.agentSym()->cast("Server"));
if (servers.count()) {
XmlTextTag<XmlParagraph> p;
p.buf() << "The workload defines " << servers.count() << " server ";
addOneOrMany(p.buf(), servers.count(), "type") << ". ";
sect << p;
for (int i = 0; i < servers.count(); ++i) {
explainServer(sect, *servers[i]);
}
} else {
XmlTextTag<XmlParagraph> p;
p.buf() << "The workload does not use any servers. ";
sect << p;
}
code << sect;
}
static
void explainInterests(XmlNodes &code, const RobotSym &robot) {
XmlText t;
Array<StringSym*> interests;
Array<double> iprobs;
if (robot.interests(interests, iprobs)) {
t.buf() << "The robot is interested in the following "
<< interests.count() << " object groups: ";
for (int i = 0; i < interests.count(); ++i) {
addAnd(t.buf(), i, interests.count());
t.buf() << '"' << interests[i]->val() << '"'
<< " (" << (100*iprobs[i]) << "%";
if (!i)
t.buf() << " of all requests";
t.buf() << ")";
}
t.buf() << ". ";
} else {
t.buf() << "The robot only requests private objects (i.e., "
<< "it does not share request URLs with other robots).";
}
code << t;
}
static
void explainRobot(XmlTag §, const RobotSym &rbt) {
XmlTextTag<XmlParagraph> p1;
RndDistr *iad = 0;
if (rbt.reqInterArrival(iad)) {
if (iad) {
p1.buf() << "Robot \"" << rbt.kind() << "\" is a \"constant "
<< "request rate\" robot ";
if (String(iad->pdfName()) == "exp") {
const double rate = 1/iad->mean();
p1.buf() << "with request rate of " << rate
<< " requests per second. ";
} else {
p1.buf() << "with request inter-arrival distribution set to ";
iad->print(p1.buf(), &DistrSym::TimeArgPrinter) << ". ";
}
} else {
p1.buf() << "Robot \"" << rbt.kind() << "\" is a \"passive\" "
<< "robot that will not submit any requests on its own. ";
}
} else {
p1.buf() << "Robot \"" << rbt.kind() << "\" is a \"best-effort\" "
<< "robot that will start next HTTP transaction as soon "
<< "as the previous transaction completes. ";
}
double ratio;
if (rbt.recurRatio(ratio)) {
p1.buf() << "This robot revisits " << (100*ratio) << "% of previously "
<< "requested URLs (offering a hit when a URL is cachable). ";
}
if (rbt.embedRecurRatio(ratio)) {
p1.buf() << "About " << (100*ratio) << "% of embedded objects "
<< " will be loaded. ";
}
if (rbt.minimizeNewConn(ratio)) {
p1.buf() << "The robot will attempt to minimize the number of new "
<< "connections (by using substitutes servers) with a probability "
<< "of " << (100*ratio) << "%. ";
}
sect << p1;
XmlTextTag<XmlParagraph> p2;
int lmt;
if (rbt.openConnLimit(lmt)) {
p2.buf() << "This robot is not allowed to open more than " << lmt;
addOneOrMany(p2.buf(), lmt, " connection") << " at any given time, "
<< "even if that limit causes decrease in request rate or "
<< "memory exhaustion. ";
if (rbt.waitXactLimit(lmt)) {
p2.buf() << "However, the robot limits waiting transaction queue ";
addOneOrMany(p2.buf() << "to " << lmt, lmt, " transaction")
<< ", cancelling newly submitted transactions as needed. ";
} else {
p2.buf() << "Moreover, waiting transaction queue can grow without bounds. ";
}
} else {
p2.buf() << "This robot opens as many connections as needed. ";
}
if (rbt.privCache(lmt)) {
p2.buf() << "Robot's private cache is limited to " << lmt;
addOneOrMany(p2.buf(), lmt, " entry", " entries") << ". ";
}
XmlNodes code;
explainAgent(code, rbt, "robot");
explainInterests(code, rbt);
explainMsgTypes(code, rbt, "request");
p2 << code;
sect << p2;
XmlTextTag<XmlParagraph> p3;
if (PopModelSym *pm = rbt.popModel()) {
const PopDistr *pd = pm->popDistr();
double hotSetFrac;
double hotSetProb;
if (pd && pm->hotSetFrac(hotSetFrac) && pm->hotSetProb(hotSetProb)) {
p3.buf() << "\"" << rbt.kind() << "\" robots direct "
<< (100*hotSetProb) << "% of all requests to "
<< (100*hotSetFrac) << "% of the working set, using ";
pd->print(p3.buf()) << " popularity distribution. ";
}
}
sect << p3;
}
static
void explainRobots(XmlNodes &code, const PglParser &) {
XmlSection sect(2, "Robots configuration");
Array<const RobotSym *> robots;
for (AgentArrIter i(PglStaticSemx::TheAgentsToUse, 0, "Robot"); i; i.nextSym())
robots.append(&(RobotSym&)i.agentSym()->cast("Robot"));
if (robots.count()) {
XmlTextTag<XmlParagraph> p;
p.buf() << "The workload defines " << robots.count() << " robot ";
addOneOrMany(p.buf(), robots.count(), "type") << ". ";
sect << p;
for (int i = 0; i < robots.count(); ++i) {
explainRobot(sect, *robots[i]);
}
} else {
XmlTextTag<XmlParagraph> p;
p.buf() << "The workload does not use any robots. ";
sect << p;
}
code << sect;
}
static
void explainContType(XmlTag §, const ContentSym &ctype) {
XmlTextTag<XmlParagraph> p;
if (RndDistr *sized = ctype.size()) {
p.buf() << "The size distribution for \"" << ctype.kind()
<< "\" content type is ";
sized->print(p.buf(), &DistrSym::SizeArgPrinter);
p.buf() << ". ";
} else {
p.buf() << "Content type \"" << ctype.kind() << "\" has unknown size. ";
}
double ratio = 0;
ctype.cachable(ratio); // default is 0
p.buf() << "About " << (100*ratio) << "% of \"" << ctype.kind()
<< "\" objects are cachable. ";
Array<ContentSym*> ccfgs;
RndDistr *selector;
Array<double> cprobs;
if (ctype.contains(ccfgs, selector, cprobs) && ccfgs.count()) {
p.buf() << "This content type is a container. Objects may "
<< "contain (embed) the following " << ccfgs.count();
addOneOrMany(p.buf(), ccfgs.count(), " content type") << ": ";
for (int i = 0; i < ccfgs.count(); ++i) {
addAnd(p.buf(), i, ccfgs.count());
p.buf() << '"' << ccfgs[i]->kind() << '"'
<< " (" << (100*cprobs[i]) << "%";
if (!i)
p.buf() << " of all embedded content";
p.buf() << ")";
}
p.buf() << ". ";
if (RndDistr *embCnt = ctype.embedCount()) {
p.buf() << "The number of embedded objects per container is "
<< "distributed as ";
embCnt->print(p.buf(), &DistrSym::IntArgPrinter) << ". ";
}
} else {
p.buf() << "This content type does not contain other types. ";
}
if (MimeSym *mime = ctype.mime()) {
const String mtype = mime->mimeType();
if (mtype) {
p.buf() << "Generated object have \"" << mtype
<< "\" MIME type. ";
}
Array<String*> pfxs;
RndDistr *sel;
if (mime->prefixes(pfxs, sel) && pfxs.count()) {
p.buf() << "URLs may start with the following " << pfxs.count();
addOneOrMany(p.buf(), pfxs.count(), " string") << ": ";
for (int x = 0; x < pfxs.count(); ++x) {
addAnd(p.buf(), x, pfxs.count());
p.buf() << '"' << *pfxs[x] << '"';
}
p.buf() << ". ";
}
Array<String*> exts;
if (mime->extensions(exts, sel) && exts.count()) {
p.buf() << "The following " << exts.count();
addOneOrMany(p.buf(), exts.count(), " extension")
<< " may appear at the end of URLs: ";
for (int e = 0; e < exts.count(); ++e) {
addAnd(p.buf(), e, exts.count());
p.buf() << '"' << *exts[e] << '"';
}
p.buf() << ". ";
}
}
sect << p;
}
static
void buildContTypeTable(XmlTable &table, const Array<const ContentSym*> &ctypes) {
table << XmlAttr::Int("border", 1) << XmlAttr("bgcolor", "#FFFFFF");
XmlTableRec hr;
hr
<< XmlTableHeading("Type")
<< XmlTableHeading("Reply Size")
<< XmlTableHeading("Cachability")
<< XmlTableHeading("Extensions");
XmlTableHeader header;
header << hr;
header << XmlAttr("bgcolor", "#BBBBBB");
table << header;
for (int i = 0; i < ctypes.count(); ++i) {
XmlTableRec rec;
const ContentSym &ctype = *ctypes[i];
rec << XmlTableHeading(ctype.kind());
XmlTextTag<XmlTableCell> cellSize;
if (RndDistr *sized = ctype.size())
sized->print(cellSize.buf(), &DistrSym::SizeArgPrinter);
else
cellSize.buf() << '?';
cellSize << XmlAttr("align", "center");
rec << cellSize;
XmlTextTag<XmlTableCell> cellChb;
double ratio = 0;
ctype.cachable(ratio);
cellChb.buf() << (100*ratio) << '%'; // default is zero
cellChb << XmlAttr("align", "right");
rec << cellChb;
XmlTextTag<XmlTableCell> cellExts;
if (MimeSym *mime = ctype.mime()) {
Array<String*> exts;
RndDistr *sel;
if (mime->extensions(exts, sel)) {
for (int e = 0; e < exts.count(); ++e) {
addAnd(cellExts.buf(), e, exts.count());
cellExts.buf() << *exts[e];
}
}
}
if (!cellExts.buf().tellp())
cellExts.buf() << " ";
rec << cellExts;
table << rec;
}
}
static
void explainContTypes(XmlNodes &code, const PglParser &) {
XmlSection sect(2, "Content types");
Array<const ContentSym*> ctypes;
{for (AgentArrIter i(PglStaticSemx::TheAgentsToUse, 0, "Server"); i; i.nextSym()) {
const ServerSym &srv = (const ServerSym&)i.agentSym()->cast("Server");
Array<ContentSym*> ccfgs;
Array<double> cprobs;
if (srv.contents(ccfgs, cprobs)) {
for (int c = 0; c < ccfgs.count(); ++c) {
bool found = false;
for (int f = 0; !found && f < ctypes.count(); ++f)
found = ctypes[f]->equal(*ccfgs[c]);
if (!found)
ctypes.append(ccfgs[c]);
}
}
}}
if (ctypes.count()) {
XmlTextTag<XmlParagraph> p;
p.buf() << "The workload uses " << ctypes.count();
addOneOrMany(p.buf(), ctypes.count(), " unique content type") << ". ";
sect << p;
XmlTag c("center");
XmlTable table;
buildContTypeTable(table, ctypes);
c << table;
sect << c;
for (int i = 0; i < ctypes.count(); ++i) {
explainContType(sect, *ctypes[i]);
}
} else {
XmlTextTag<XmlParagraph> p;
p.buf() << "The workload does not use any content types. ";
sect << p;
}
code << sect;
}
int main(int argc, char **argv) {
(void)PolyVersion();
if (argc < 2)
cerr << "usage: " << argv[0] << " <input_file> [include_dir] ..." << endl << xexit;
for (int i = 2; i < argc; ++i)
PglPp::TheDirs.append(new String(argv[i]));
PglStaticSemx::Interpret(argv[1]);
PglParser parser;
XmlNodes code;
explainPhases(code, parser);
explainServers(code, parser);
explainRobots(code, parser);
explainContTypes(code, parser);
configureStream(cout, 2);
code.print(cout, "");
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1