/* Web Polygraph http://www.web-polygraph.org/
* (C) 2003-2006 The Measurement Factory
* Licensed under the Apache License, Version 2.0 */
#include "base/polygraph.h"
#include <ctype.h>
#include "xstd/h/iostream.h"
#include "xstd/h/iomanip.h"
#include "xstd/BitMask.h"
#include "xstd/Ssl.h"
#include "base/ObjId.h"
#include "base/RndPermut.h"
#include "base/BStream.h"
#include "base/AddrParsers.h"
#include "base/polyLogCats.h"
#include "base/polyLogTags.h"
#include "runtime/AddrMap.h"
#include "runtime/HostMap.h"
#include "runtime/PubWorld.h"
#include "runtime/HttpCookies.h"
#include "runtime/StatPhase.h"
#include "runtime/StatPhaseMgr.h"
#include "runtime/LogComment.h"
#include "runtime/ErrorMgr.h"
#include "runtime/EphPortMgr.h"
#include "runtime/ExpPortMgr.h"
#include "runtime/PersistWorkSetMgr.h"
#include "runtime/PopModel.h"
#include "runtime/polyBcastChannels.h"
#include "runtime/polyErrors.h"
#include "runtime/StatPhaseMgr.h"
#include "runtime/globals.h"
#include "runtime/SslWrap.h"
#include "runtime/SslWraps.h"
#include "csm/ContentSel.h"
#include "csm/ContentCfg.h"
#include "csm/ContentMgr.h"
#include "csm/oid2Url.h"
#include "pgl/RobotSym.h"
#include "dns/DnsMgr.h"
#include "client/CltConnMgr.h"
#include "client/CltOpts.h"
#include "client/CltXact.h"
#include "client/ForeignWorld.h"
#include "client/IcpCltXact.h"
#include "client/SessionMgr.h"
#include "client/PrivWorlds.h"
#include "client/PrivCache.h"
#include "client/UserCred.h"
#include "client/CltCfg.h"
#include "client/Client.h"
class IcpAgentCfg;
struct SharedCfgItem { const RobotSym *sym; CltCfg *cfg; };
static Array<SharedCfgItem> SharedCfgs;
XactFarm<CltXact> *Client::TheXacts = 0;
ObjFarm<IcpCltXact> Client::TheIcpXacts;
CltSharedCfgs *Client::TheSharedCfgs = new CltSharedCfgs;
Array<PortMgr*> Client::ThePortMgrs;
Client::Client(): thePrivCache(0), theAuthOrigins(0), theCfg(0),
theConnMgr(0), theDnsMgr(0), theSessionMgr(0), theIcpClient(0),
theCcXactLvl(0), theExtraLaunchLvl(0), theCookiesKeepLimit(0),
authProxy(false), isIdle(false) {
theChannels.append(TheInfoChannel);
theChannels.append(TheLogCfgChannel);
theChannels.append(TheLogStateChannel);
startListen();
}
Client::~Client() {
delete theConnMgr;
delete theDnsMgr;
delete theSessionMgr;
delete thePrivCache;
delete theAuthOrigins;
}
void Client::configure(const RobotSym *cfg, const NetAddr &aHost) {
Assert(TheXacts);
Assert(aHost.port() < 0); // remove later
SockOpt opt;
Agent::configure(cfg, aHost, opt);
theCfg = TheSharedCfgs->getConfig(cfg);
configurePrivWorlds();
int pcCap = 0;
if (cfg->privCache(pcCap))
thePrivCache = new PrivCache(pcCap);
theCfg->selectProxy(theProxyAddr); // sticky selection
theConnMgr = new CltConnMgr;
theConnMgr->configure(opt, cfg, theProxyAddr ? 1 : theCfg->viservLimit());
theConnMgr->portMgr(getPortMgr(true));
theConnMgr->idleTimeout(cfg->idlePconnTimeout());
const SslWrap *sslWrap = 0;
if (theCfg->selectSslWrap(sslWrap)) { // sticky selection
theSslCtx = sslWrap->makeClientCtx(theHost);
theConnMgr->configureSsl(theSslCtx, sslWrap);
}
theDnsMgr = new DnsMgr(this);
theDnsMgr->configure(cfg->dnsResolver());
if (theCfg->theBusyPeriod) {
theSessionMgr = new SessionMgr(this);
theSessionMgr->configure(theCfg);
}
if (theCfg->thePeerHttp && !theCfg->thePeerIcp) {
cerr << cfg->loc() << "the HTTP peer is at " << theCfg->thePeerHttp << ", but where is the ICP peer?" << endl;
exit(-3);
}
isCookieSender = theCfg->selectCookieSenderStatus();
if (isCookieSender) {
theCookiesKeepLimit = theCfg->theCookiesKeepLimitSel ?
(int)theCfg->theCookiesKeepLimitSel->trial() : 4;
}
TheXacts->limit(1024); // magic, no good way to estimate
}
void Client::icpClient(IcpClient *anIcpClient) {
Assert(!theIcpClient);
theIcpClient = anIcpClient;
}
void Client::start() {
Assert(theConnMgr);
Assert(theDnsMgr);
Agent::start();
theDnsMgr->start();
theCfg->startWarmup(); // warmup plan created if needed
if (theSessionMgr)
theSessionMgr->start();
else
becomeBusy();
}
void Client::stop() {
if (theSessionMgr)
theSessionMgr->stop();
else
if (!isIdle)
becomeIdle();
theDnsMgr->stop();
Agent::stop();
}
void Client::becomeBusy() {
isIdle = false;
selectHttpVersion(*theCfg);
theMemberships.reset();
theCfg->selectCredentials(theCredentials);
theCfg->findMemberships(theCredentials, theMemberships);
Broadcast(TheSessionBegChannel, this);
scheduleLaunch(TheClock);
}
void Client::continueSession() {
Broadcast(TheSessionCntChannel, this);
}
void Client::becomeIdle() {
isIdle = true;
if (thePrivCache)
thePrivCache->clear();
if (theAuthOrigins)
theAuthOrigins->clear();
authProxy = false;
while (theLaunchDebts.count())
launchCanceled(dequeSuspXact());
theExtraLaunchLvl = 0;
theDnsMgr->clearCache();
theConnMgr->closeAllIdle();
Broadcast(TheSessionEndChannel, this);
}
void Client::describe(ostream &os) const {
Agent::describe(os);
if (theProxyAddr)
os << " via " << theProxyAddr;
}
void Client::noteInfoEvent(BcastChannel *ch, InfoEvent ev) {
Assert(ch == TheInfoChannel);
if (ev == ieWssFreeze && !PrivWorld::Frozen()) {
for (PrivWorldIterator p(this); !p.atEnd(); ++p)
p.privWorld().freezeWss();
if (PrivWorld::Frozen()) {
Comment(6) << "fyi: all private working sets are now frozen" << endc;
ReportWss(5);
}
}
}
void Client::noteLogEvent(BcastChannel *ch, OLog &log) {
if (ch == TheLogCfgChannel) {
log << bege(lgCltCfg, lgcCltSide);
// XXX: implement (CltCfgRec)
log << ende;
} else
if (ch == TheLogStateChannel) {
log << bege(lgCltState, lgcCltSide)
<< theSeqvId
<< thePrivWorlds
<< ende;
}
}
void Client::noteXactDone(CltXact *x) {
Assert(x);
if (!Should(x->conn()))
return; // XXX: fix me
const ObjId oid(x->oid());
// XXX: request type may be different this time
CltXact *retry = shouldRetry(x) ? genXact(oid, x) : 0;
if (theSessionMgr)
theSessionMgr->noteXactDone(x);
if (!Should(x->conn()))
return; // XXX: fix me
if (isIdle)
x->conn()->lastUse(true); // close all connections if we are idling
theConnMgr->put(x->conn());
putXact(x);
theCcXactLvl--;
Assert(theCcXactLvl >= 0);
// no xaction should restart when we are idle
if (isIdle) {
Should(!retry);
return;
}
if (retry && tryLaunch(retry))
return;
// push waiting xactions forward
if (theLaunchDebts.count() && !theConnMgr->atHardConnLimit()) {
resumeXact();
return;
}
if (theCcXactLvl == 0)
loneXactFollowup();
}
void Client::loneXactFollowup() {
}
CltXact *Client::fetch(const ObjId &oid, DistrPoint *dp) {
Assert(oid);
CltXact *x = genXact(oid, 0);
x->cacheDistrPoint(dp);
if (tryLaunch(x))
return x;
return 0;
}
// must not be called directly but rather through tryLaunch
bool Client::launch(CltXact *x) {
Assert(x);
Connection *conn = x->conn(); // non-zero if pipelining
if (!conn) {
Assert(!theConnMgr->atHardConnLimit());
const NetAddr &connAddr = x->nextHop();
Assert(connAddr);
Assert(!connAddr.isDomainName());
const NetAddr destAddr = Oid2UrlHost(x->oid());
conn = theConnMgr->get(connAddr, destAddr);
if (!conn && ReportError(errConnectEstb)) {
Comment(1) << theHost << " failed to connect to " << connAddr <<
endc;
}
}
if (conn) {
theCcXactLvl++;
x->exec(this, conn);
return true;
}
launchFailed(x);
return false;
}
// tries to generate and launch a transaction
// returns true if the xaction will be launched [eventually]
bool Client::tryLaunch() {
if (theExtraLaunchLvl) {
--theExtraLaunchLvl;
return false;
}
return tryLaunch(genXact());
}
// will launch the xaction (if possible) or postpone it (if not)
// returns true if the xaction will be launched [eventually]
bool Client::tryLaunch(CltXact *x) {
Assert(x);
if (isIdle)
return launchCanceled(x);
if (!x->nextHop()) {
// should we ask peers for the best server?
// XXX: remove askedPeer; the x->nextHop() should be enough?
if (theCfg->thePeerIcp && !x->askedPeer()) {
askPeer(theCfg->thePeerIcp, x);
return true;
}
// next hop address should be known at this time
// (but my not be resolved yet)
if (!setNextHopAddr(x))
return false;
// should we lookup the next hop address?
if (theDnsMgr->needsLookup(x->nextHop())) {
// async call unless fails immediately
if (lookupAddr(x))
return true;
launchFailed(x);
return false;
}
}
// check if we should postpone the xaction
if (!x->conn() && theConnMgr->atHardConnLimit())
return suspendXact(x);
return launch(x);
}
bool Client::suspendXact(CltXact *x) {
if (theCfg->theWaitXactLmt < 0 || theLaunchDebts.count() < theCfg->theWaitXactLmt) {
theLaunchDebts.append(x);
Broadcast(TheWaitBegChannel, x);
return true;
}
if (ReportError(errTooManyWaitXact)) {
Comment(3) << "xactions active: " << theCcXactLvl
<< " waiting: " << theLaunchDebts.count()
<< " limit: " << theCfg->theWaitXactLmt << endc;
}
launchFailed(x);
return false;
}
void Client::resumeXact() {
launch(dequeSuspXact());
}
CltXact *Client::dequeSuspXact() {
CltXact *x = theLaunchDebts.pop();
Broadcast(TheWaitEndChannel, x);
return x;
}
bool Client::launchCanceled(CltXact *x) {
Assert(x);
x->noteAbort();
putXact(x);
return false;
}
bool Client::launchFailed(CltXact *x) {
Assert(x);
if (!isIdle)
x->countFailure();
return launchCanceled(x);
}
void Client::putXact(CltXact *x) {
// recycle x and xactions that caused it
// stop if a xaction still has kids
while (x && x->finished() && x->childCount() == 0) {
CltXact *cause = x->cause();
if (cause)
cause->noteChildGone(x);
TheXacts->put(x);
x = cause;
}
}
CltXact *Client::genXact(const ObjId &oid, CltXact *cause) {
CltXact *x = TheXacts->get();
x->oid(oid);
if (cause) {
cause->noteChildNew(x);
x->cause(cause);
}
return x;
}
CltXact *Client::genXact() {
ObjId oid;
genOid(oid);
return genXact(oid, 0);
}
void Client::genOid(ObjId &oid) {
const int interest = theCfg->selectInterest();
if (interest == OidGenStat::intForeign) {
selectForeignObj(oid);
} else {
selectViserv(oid);
selectTarget(oid);
selectObj(oid, interest);
selectContType(oid);
}
selectReqType(oid);
selectReqMethod(oid);
if (theCfg->genUniqUrls)
oid.world(UniqId::Create()); // changes every time
}
void Client::selectViserv(ObjId &oid) {
const int viserv = theCfg->selectViserv();
HostCfg *host = TheHostMap->at(viserv);
Assert(host);
Assert(host->thePubWorld);
Assert(host->theServerRep);
oid.viserv(viserv);
int limit = 0;
if (doCookies(limit) && !host->theCookies)
host->theCookies = new HttpCookies(limit);
}
void Client::selectTarget(ObjId &oid) {
const NetAddr &visName = TheHostMap->at(oid.viserv())->theAddr;
int niamIdx; // name in AddrMap index
Assert(TheAddrMap->find(visName, niamIdx));
if (oid.type() < 0)
selectAnyTarget(oid, niamIdx);
else
selectTypedTarget(oid, niamIdx);
// sanity checks
const HostCfg *host = TheHostMap->at(oid.target());
Assert(host);
Assert(host->theContent);
}
void Client::selectObj(ObjId &oid, int interest) {
static RndGen rng;
OidGenStat &oidGenStat = TheStatPhaseMgr->oidGenStat();
const bool needPub = interest == OidGenStat::intPublic;
const bool needRepeat = rng.event(theCfg->theRecurRatio*
TheStatPhaseMgr->recurFactor().current());
oidGenStat.recordNeed(needRepeat, interest);
PrivWorld &privWorld = thePrivWorlds[oid.viserv()];
PubWorld &pubWorld = *TheHostMap->at(oid.viserv())->thePubWorld;
const bool privCanRep = privWorld.canRepeat();
const bool privCanProd = privWorld.canProduce();
const bool pubCanRep = pubWorld.canRepeat();
const bool pubCanProd = pubWorld.canProduce();
const bool canRep = privCanRep || pubCanRep;
const bool canProd = privCanProd || pubCanProd;
// the logic gives priority to repeatOid goal rather than to genPubOid
if (canRep && (needRepeat || !canProd)) {
if (pubCanRep && (needPub || !privCanRep)) {
pubWorld.repeat(oid, theCfg->thePopModel);
oidGenStat.recordGen(true, OidGenStat::intPublic);
return;
}
Assert(privCanRep);
privWorld.repeat(oid, theCfg->thePopModel);
oidGenStat.recordGen(true, OidGenStat::intPrivate);
return;
}
// new public
if (pubCanProd && (needPub || !privCanProd)) {
pubWorld.produce(oid, rng);
oidGenStat.recordGen(false, OidGenStat::intPublic);
return;
}
// new private object (last resort, never fails)
privWorld.produce(oid, rng);
oidGenStat.recordGen(false, OidGenStat::intPrivate);
}
void Client::selectContType(ObjId &oid) {
Assert(oid.type() < 0); // we do not overwrite existing setting
const HostCfg *hcfg = TheHostMap->at(oid.target());
Assert(hcfg);
Assert(hcfg->theContent);
const ContentCfg *ccfg = hcfg->theContent->getDir(oid);
oid.type(ccfg->id());
}
void Client::selectReqType(ObjId &oid) {
static RndGen rng;
const int reqType =
rng.event(TheStatPhaseMgr->specialMsgFactor().current()) ?
(int)theCfg->theReqTypeSel->trial() : rqtBasic;
oid.ims200(reqType == rqtIms200);
oid.ims304(reqType == rqtIms304);
oid.reload(reqType == rqtReload);
oid.rediredReq(false);
}
void Client::selectReqMethod(ObjId &oid) {
static RndGen rng;
const int reqMethod =
rng.event(TheStatPhaseMgr->specialMsgFactor().current()) ?
(int)theCfg->theReqMethodSel->trial() : rqmGet;
oid.get(reqMethod == rqmGet);
oid.post(reqMethod == rqmPost);
oid.head(reqMethod == rqmHead);
oid.put(reqMethod == rqmPut);
}
// find any target behind a visible name
void Client::selectAnyTarget(ObjId &oid, int niamIdx) {
const NetAddr &targetAddr = TheAddrMap->selectAddr(niamIdx);
int targetIdx = -1;
Assert(TheHostMap->find(targetAddr, targetIdx));
oid.target(targetIdx);
}
// find a target that has requested oid type
void Client::selectTypedTarget(ObjId &oid, int niamIdx) {
Assert(oid.type() > 0);
for (AddrMapAddrIter i = TheAddrMap->addrIter(niamIdx); i; ++i) {
int targetIdx = -1;
Assert(TheHostMap->find(i.addr(), targetIdx));
const HostCfg *hcfg = TheHostMap->at(targetIdx);
Assert(hcfg && hcfg->theContent);
if (hcfg->theContent->hasContType(oid.type())) {
oid.target(targetIdx);
return;
}
}
// we failed to find a target that has the right content
if (ReportError(errUnreachContType)) {
static const String strUndefined = "undefined";
const NetAddr &visName = TheHostMap->at(oid.viserv())->theAddr;
const String kind = TheContentMgr.get(oid.type())->kind() ?
TheContentMgr.get(oid.type())->kind() : strUndefined;
Comment << "robot at " << host() << " cannot find content of '"
<< kind << "' kind on server(s) visible as " << visName << endc;
}
selectAnyTarget(oid, niamIdx);
}
void Client::selectForeignObj(ObjId &oid) {
Assert(theCfg->foreignWorld());
ForeignWorld &foreignWorld = *theCfg->foreignWorld();
static RndGen rng;
OidGenStat &oidGenStat = TheStatPhaseMgr->oidGenStat();
const bool needRepeat = rng.event(theCfg->theRecurRatio*
TheStatPhaseMgr->recurFactor().current());
oidGenStat.recordNeed(needRepeat, OidGenStat::intForeign);
const bool canRep = foreignWorld.canRepeat();
const bool canProd = foreignWorld.canProduce();
if (canRep && (needRepeat || !canProd)) {
foreignWorld.repeat(oid, theCfg->thePopModel);
oidGenStat.recordGen(true, OidGenStat::intForeign);
return;
}
foreignWorld.produce(oid, rng);
oidGenStat.recordGen(false, OidGenStat::intForeign);
}
bool Client::credsForOrigin(const ObjId &oid, UserCred &cred) const {
if (!theAuthOrigins || !theAuthOrigins->isSet(oid.viserv()))
return false;
return genCredentials(cred);
}
bool Client::credsForProxy(const ObjId &, UserCred &cred) const {
if (!authProxy)
return false;
return genCredentials(cred);
}
bool Client::genCredentials(UserCred &cred) const {
cred = UserCred(theCredentials);
static RndGen rng;
if (cred.image() && rng.event(theCfg->theAuthError))
cred.invalidate();
return cred.image();
}
bool Client::doCookies(int &limit) const {
if (isCookieSender && theCookiesKeepLimit > 0) {
limit = theCookiesKeepLimit;
return true;
} else {
limit = 0;
return false;
}
}
bool Client::shouldRetry(const CltXact *x) const {
// do not retry if we became idle while trying
if (isIdle)
return false;
// count the number of consequitive retries
int rcount = 0;
while (x && x->needRetry()) {
rcount++;
x = x->cause();
}
if (rcount > 10) {
ReportError(errManyRetries);
return false;
}
return rcount > 0;
}
void Client::noteOriginAuthReq(CltXact *cause) {
if (!theAuthOrigins)
theAuthOrigins = new BitMask(theCfg->viservLimit());
theAuthOrigins->beSet(cause->oid().viserv());
}
void Client::noteProxyAuthReq(CltXact *cause) {
authProxy = true;
}
void Client::noteRedirect(CltXact *cause, const ObjId &oid) {
Assert(oid.rediredReq());
if (tryLaunch(genXact(oid, cause)))
theExtraLaunchLvl++;
}
void Client::noteEmbedded(CltXact *parent, const ObjId &oid) {
static RndGen rng;
if (!rng.event(theCfg->theEmbedRecurRatio))
return;
if (thePrivCache && thePrivCache->loadOid(oid))
return; // hit, no need to request oid
CltXact *child = genXact(oid, parent);
child->page(parent->page());
// simulate cached DNS responses for embedded objects
if (!oid.foreignUrl() && oid.viserv() == parent->oid().viserv())
child->nextHopVar() = parent->nextHop();
// pipeline if possible
if (child->nextHop() && child->nextHop() == parent->nextHop()) {
if (CltXactMgr *mgr = parent->getPipeline())
child->pipeline(mgr);
}
if (tryLaunch(child))
theExtraLaunchLvl++;
}
void Client::askPeer(const NetAddr &addr, CltXact *x) {
IcpCltXact *q = TheIcpXacts.get();
q->reason(x);
q->exec(this, addr);
}
void Client::notePeerAsked(IcpCltXact *q) {
Assert(q);
CltXact *x = q->reason(this);
x->usePeer(q->hit());
/* pass ICP stats here ... */
TheIcpXacts.put(q);
tryLaunch(x);
}
bool Client::lookupAddr(CltXact *x) {
return theDnsMgr->lookup(x->nextHop(), x);
}
void Client::noteAddrLookup(const NetAddr &addr, CltXact *x) {
Assert(x);
// successful lookup?
if (addr) {
x->nextHopVar() = addr; // update
tryLaunch(x);
} else {
launchFailed(x);
}
}
// select the address for the next hop connection
bool Client::setNextHopAddr(CltXact *x) const {
NetAddr &addr = x->nextHopVar();
if (x->usePeer() && theCfg->thePeerHttp)
return addr = theCfg->thePeerHttp;
if (theProxyAddr)
return addr = theProxyAddr;
if (x->oid().foreignUrl()) {
const char *uri = x->oid().foreignUrl().cstr();
if (!SkipHostInUri(uri, uri+x->oid().foreignUrl().len(), addr)) {
if (ReportError(errHostlessForeignUrl))
Comment << "foreign URL: " << uri << endc;
return false;
}
return addr;
}
return addr = TheHostMap->at(x->oid().viserv())->theAddr;
}
PortMgr *Client::getPortMgr(bool bind2iface) {
const NetAddr addr = bind2iface ? theHost : NetAddr();
// check if we already have a port mgr for host
for (int i = 0; i < ThePortMgrs.count(); ++i) {
if (ThePortMgrs[i]->addr() == addr)
return ThePortMgrs[i];
}
// create new port manager
PortMgr *mgr = TheCltOpts.thePorts.set() ?
(PortMgr*) new ExpPortMgr(addr, TheCltOpts.thePorts.lo(), TheCltOpts.thePorts.hi()) :
(PortMgr*) new EphPortMgr(addr);
ThePortMgrs.append(mgr);
return mgr;
}
// configure private worlds, one for each [unique] origin
void Client::configurePrivWorlds() {
thePrivWorlds.stretch(theCfg->viservLimit());
thePrivWorlds.count(theCfg->viservLimit());
for (PrivWorldIterator p(this); !p.atEnd(); ++p) {
if (!p.privWorld()) {
p.privWorld() = PrivWorld(theId);
PrivWorld::TheTotalCount++;
}
}
}
void Client::loadWorkingSet(IBStream &is) {
static bool loadedWss = false;
if (!loadedWss) {
is >> PrivWorld::TheWss;
PrivWorld::TheFrozenCount = 0;
loadedWss = true;
}
Agent::loadWorkingSet(is);
const int cfgCount = PrivWorldIterator(this).count();
const int storedCount = is.geti();
ThePersistWorkSetMgr.checkInput();
if (storedCount != cfgCount) {
Comment << "warning: robot #" << theSeqvId << " used to talk to " <<
storedCount << " origin servers when working set was " <<
"stored but is configured to talk to " << cfgCount <<
" (possibly different) servers now" << endc;
}
// load private worlds, one at a time
for (int i = 0; i < storedCount; ++i) {
NetAddr visName;
PrivWorld privWorld;
is >> visName >> privWorld;
ThePersistWorkSetMgr.checkInput();
int viserv = -1;
if (TheHostMap->find(visName, viserv) && theCfg->hasViserv(viserv)) {
if (!Should(viserv < thePrivWorlds.count())) {
thePrivWorlds.stretch(viserv+1);
thePrivWorlds.count(viserv+1);
}
if (!Should(thePrivWorlds[viserv]))
PrivWorld::TheTotalCount++;
thePrivWorlds.put(privWorld, viserv);
// update frozen counter, assume default worlds were not frozen
if (privWorld.wss() >= 0)
PrivWorld::TheFrozenCount++;
} else {
Comment << "error: visible server " << visName << " in the " <<
"private working set (being loaded for robot " << host() <<
"from " << is.name() << ") is not in current robot's " <<
"origins configuration, skipping" << endc;
}
}
}
void Client::storeWorkingSet(OBStream &os) {
static bool storedWss = false;
if (!storedWss) {
os << PrivWorld::TheWss;
storedWss = true;
}
Agent::storeWorkingSet(os);
PrivWorldIterator p(this);
os << p.count();
for (; !p.atEnd(); ++p)
os << p.addr() << p.privWorld();
}
void Client::missWorkingSet() {
// should we freeze all private worlds?
}
int Client::logCat() const {
return lgcCltSide;
}
void Client::ReportWss(int commentLvl) {
const BigSize meanFillSz =
(*TheStatPhaseMgr && TheStatPhaseMgr->fillCnt()) ?
TheStatPhaseMgr->fillSz()/TheStatPhaseMgr->fillCnt() :
BigSize(0);
ostream &os = Comment(commentLvl) <<
"fyi: min 'direct' objects in working set:" << endl;
int pubFrozenCount = 0;
int pubTotalCount = 0;
const int pubWss = PubWorld::CurrentWss(pubFrozenCount, pubTotalCount);
os << "\tglobal public: " << pubWss << " (";
if (meanFillSz > 0)
os << "~" << (meanFillSz*pubWss) << " size, ";
os << pubFrozenCount << '/' << pubTotalCount << '=' <<
Percent(pubFrozenCount, pubTotalCount) << "% frozen slices)" <<
endl;
const int privFrozenCount = PrivWorld::TheFrozenCount;
const int privTotalCount = PrivWorld::TheTotalCount;
const int privWss = PrivWorld::TheWss;
os << "\tlocal private: " << privWss << " (";
if (meanFillSz > 0)
os << "~" << (meanFillSz*privWss) << " size, ";
os << privFrozenCount << '/' << privTotalCount << '=' <<
Percent(privFrozenCount, privTotalCount) << "% frozen slices)"
;
os << endc;
}
void Client::Farm(XactFarm<CltXact> *aFarm) {
Assert(!TheXacts && aFarm);
TheXacts = aFarm;
}
void Client::LogState(OLog &log) {
log << bege(lgSrvRepState, lgcCltSide);
// XXX: remove state logging?
// OLogStorePtrs(log, ThePubWorlds);
log << ende;
}
syntax highlighted by Code2HTML, v. 0.9.1