Game Development Community

dev|Pro Game Development Curriculum

Instrumenting BitStream

by Orion Elenzil · 04/18/2007 (1:55 pm) · 5 comments

This resource provides a framework around instrumenting BitStream.
It can help you answer questions like
"How many bits are going out to each client ?"
"How many of those bits are in Player ?"
"How many are due to the MoveMask ?"
"How many are due to MyCrazyJankyCustomNetmask ?"
etc

all the source code changes are provided below,
but the commentary assumes familiarity with C++ and TGE.

NOTE - this uses a non-stock method, ScriptObject::getDebugString(),
which i now realize i haven't provided as a resource.
Apologies for that. i'll try to resourcify it soon.
In the meantime, i'll provide alternative code which doesn't use it.


bitStream.h
at the top, add this:
#ifndef _TVECTOR_H_
#include "core/tVector.h"
#endif

#ifdef DEBUG_BITSTREAMCOUNT
// with ScriptObject::GetDebugString():
//#define BITSTREAMCOUNTER(      objName, stream, counterName, pNetCon, pSimObject) char s[256]; dSprintf(s, sizeof(s), "%-60s %-60s", pNetCon->getDebugString(), pSimObject->getDebugString()); BitStream::Counter objName(stream, counterName, s);
//#define BITSTREAMCOUNTER_NOOBJ(objName, stream, counterName, pNetCon            ) char s[256]; dSprintf(s, sizeof(s), "%-60s"      , pNetCon->getDebugString()                              ); BitStream::Counter objName(stream, counterName, s);

// without ScriptObject::GetDebugString():
#define BITSTREAMCOUNTER(      objName, stream, counterName, pNetCon, pSimObject) char s[256]; dSprintf(s, sizeof(s), "0x%-0.8X %-0.8X", pNetCon, pSimObject); BitStream::Counter objName(stream, counterName, s);
#define BITSTREAMCOUNTER_NOOBJ(objName, stream, counterName, pNetCon            ) char s[256]; dSprintf(s, sizeof(s), "0x%-0.8X"       , pNetCon            ); BitStream::Counter objName(stream, counterName, s);
#else
#define BITSTREAMCOUNTER(      objName, stream, counterName, pSimObject, pNetCon)
#define BITSTREAMCOUNTER_NOOBJ(objName, stream, counterName,             pNetCon)
#endif

inside the class BitStream, add this:
(i added it at the top)
//-------------------------------------- Bitstream Counter Stuff Begin
public:
   class Counter
   {
   public:
       Counter(BitStream* bs, const char* name, const char* desc);
      ~Counter();

   public:
      BitStream*  mBitStream;
      char        mName[64 ];
      char        mDesc[256];
      S32         mBitCount;
      S32         mBitCountChildren;
      static bool smActive;
   };

protected:
   Vector<Counter *> mCounterStack;
   void counterPush(Counter& counter);
   void counterPop (Counter& counter);
//-------------------------------------- Bitstream Counter Stuff Done


bitStream.cc
at the bottom of BitStream::writeBits(), add this:
#ifdef DEBUG_BITSTREAMCOUNT
   if (mCounterStack.size() > 0)
      mCounterStack[0]->mBitCount += bitCount;
#endif

at the bottom of BitStream::writeFlag(), add this:
#ifdef DEBUG_BITSTREAMCOUNT
   if (mCounterStack.size() > 0)
      mCounterStack[0]->mBitCount += 1;
#endif

at the bottom of the file, add this:
//-------------------------------------- Bitstream Counter Stuff Begin
// USAGE
// To use the bitstream instrumentation,
// you first gotta compile the application with DEBUG_BITSTREAMCOUNT defined.
// Then, you instrument code at the scope level via either
// BITSTREAMCOUNTER(Literal <Counter Object Name>, BitStream* bitstream, const char* CounterName, NetConnection* netConnection, SimObject* simObject)
// or
// BITSTREAMCOUNTER(Literal <Counter Object Name>, BitStream* bitstream, const char* CounterName, NetConnection* netConnection)
// where:
// * Counter Object Name is the name of the actual instance of the counter object which will be created.
//   since it's assumed that the macro is called only once per scope, it's okay to always use the same thing. i recommend "BSCounter".
// * bitStream           is a pointer to the bitstream in question.
// * counterName         will be printed to the log. Something short like "SkinMask".
// * netConnection       is a pointer to the netConnection this bitstream is going out over. used in the debug print.
//   todo - probably don't need both bitStream AND netConnection, huh.
// * simObject           is a pointer to an object [being ghosted]. used in the debug print.
//
// By "scope" is meant that an object will be created in the given scope,
// and thus automatically destroyed when it leaves scope, which will yield a debug print.
// 
// Once all this is compiled,
// active the logging in script by issuing "$bitStreamCount = true;".
//
// the resulting debug prints will look something like this:
//
// bitCount  119/119  | Player::packUpdate                 4680-AIPlayer-annie-s-annie                                  4757-GameConnection-LocalClientConnection-doris lessing     
// bitCount  119/119  | Player::packUpdate                 4676-AIPlayer-alberta-s-alberta                              4757-GameConnection-LocalClientConnection-doris lessing     
// bitCount  198/198  | Player::packUpdate                 4690-AIPlayer-Bixxy-s-Bixxy                                  4757-GameConnection-LocalClientConnection-doris lessing     
// bitCount  119/119  | Player::packUpdate                 4682-AIPlayer-frank-s-frank                                  4757-GameConnection-LocalClientConnection-doris lessing     
// bitCount  119/119  | Player::packUpdate                 4668-AIPlayer-bob-s-bob                                      4757-GameConnection-LocalClientConnection-doris lessing     
// bitCount  111/111  | Player::packUpdate                 4678-AIPlayer-djLarsBerg-s-djLarsBerg                        4757-GameConnection-LocalClientConnection-doris lessing     
// bitCount  111/111  | Player::packUpdate                 4686-AIPlayer-menace-s-menace                                4757-GameConnection-LocalClientConnection-doris lessing     
// bitCount   39/935  |netconnection::writePacket          4757-GameConnection-LocalClientConnection-doris lessing     
//
// what to look for:
// first, note that lines are printed in sort of reverse order. earlier lines are actually deeper scopes.
// so in the above, all the playe::packUpdates() are subsets of netConnnection::writePacket.
// next, the indenting. see how Player.. is indented compared to netConnection.. ?
// that reflects the scope depth.
// next, the bitcounts are: <Bits contributed by this scope>/<Bits from child scopes>.
// so in the above, netconnection::writePacket itself contributes 39 bits to a total size of 935 bits.
// the fact that everything above adds up to the final 935 value is just lucky.
// - had say some non-player object been ghosted out, things wouldn't have added up. that's okay.
// if the bitcount contributed by a scope is zero, it doesn't print a line.
//



bool BitStream::Counter::smActive = false;

BitStream::Counter::Counter(BitStream* bs, const char* name, const char* desc)
{
#ifndef DEBUG_BITSTREAMCOUNT
   AssertFatal(false, "BitStream::Counter::Counter() - shouldn't be in here.");
#endif

   AssertFatal(bs != NULL, "null bitstream in counter creation");
   dStrncpy(mName, name, sizeof(mName));
   dStrncpy(mDesc, desc, sizeof(mDesc));
   mBitCount         = 0;
   mBitCountChildren = 0;
   mBitStream        = bs;
   mBitStream->counterPush(*this);
}

BitStream::Counter::~Counter()
{
   mBitStream->counterPop(*this);
}

void BitStream::counterPush(Counter& counter)
{
#ifndef DEBUG_BITSTREAMCOUNT
   AssertFatal(false, "BitStream::counterPush() - shouldn't be in here.");
#endif
   mCounterStack.push_front(&counter);
}

void BitStream::counterPop (Counter& counter)
{
#ifndef DEBUG_BITSTREAMCOUNT
   AssertFatal(false, "BitStream::counterPop() - shouldn't be in here.");
#endif
   if (&counter != mCounterStack[0])
   {
      Con::errorf(ConsoleLogEntry::Communication, "BitStream::counterPop() - wrong counter! %s", counter.mName);
      return;
   }

   S32 bitCountTotal = counter.mBitCount + counter.mBitCountChildren;

   if (Counter::smActive && counter.mBitCount > 0)
   {
      // must.. line.. stuff.. up..
      char s[512];
      dSprintf(s, sizeof(s), "%-*s%s", mCounterStack.size() - 1, "", counter.mName);
      Con::debugf(ConsoleLogEntry::Communication, "bitCount %4d/%-4d |%-35s %s", counter.mBitCount, bitCountTotal, s, counter.mDesc);
   }

   mCounterStack.pop_front();

   if (mCounterStack.size() > 0)
      mCounterStack[0]->mBitCountChildren += bitCountTotal;
}

//-------------------------------------- Bitstream Counter Stuff Done


game.cc
at the bottom of GameInit(), add:
Con::addVariable("bitStreamCount"                    , TypeBool, &BitStream::Counter::smActive);


define DEBUG_BITSTREAMCOUNT.
In VisualStudio, do this in Project Settings | C++ | Preprocessor
by just tacking on ";DEBUG_BITSTREAMCOUNT" to "Preprocessor definitions".
For other build environments i'm sure it's similarly straight-forward.

.. that's it for the framework.



here's a couple usage examples.

netConnection.cc
at the top of NetConnection::WritePacket(), add:
BITSTREAMCOUNTER_NOOBJ(BSCounter, bstream, "netconnection::writePacket", this)

player.cc
at the top of Player::packUpdate(), add:
BITSTREAMCOUNTER(BSCounter, stream, "Player::packUpdate", con, this)

in Player::packUpdate(), find the MoveMask code block and make it look like this:
if (stream->writeFlag(mask & MoveMask)) 
   {
      [b]BITSTREAMCOUNTER(BSCounter, stream, "MoveMask", con, this)[/b]

to activate the instrumentation,
enter the console command "$BitStreamCount = true;".

obviously this is server-side.

#1
04/04/2007 (3:12 pm)
I implemented a bit counter inside BitStream a while ago but it is far from the usefullness of this resource. As always, impressive work Orion and thanks for helping the community and our projects.
#2
04/18/2007 (1:55 pm)
Nice resource
#3
04/21/2007 (10:02 am)
Very helpful.
Thank you for sharing this...
#4
04/23/2007 (1:08 pm)
Awesome
#5
05/10/2009 (11:51 pm)
very very Good~~~