text: // top.cpp
  /*
   *    Copyright (C) 2010 10gen Inc.
   *
   *    This program is free software: you can redistribute it and/or  modify
   *    it under the terms of the GNU Affero General Public License, version 3,
   *    as published by the Free Software Foundation.
   *
   *    This program is distributed in the hope that it will be useful,
   *    but WITHOUT ANY WARRANTY; without even the implied warranty of
   *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *    GNU Affero General Public License for more details.
   *
   *    You should have received a copy of the GNU Affero General Public License
   *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  
  
  #include "pch.h"
  #include "top.h"
  #include "../../util/message.h"
  #include "../commands.h"
  
  namespace mongo {
  
      Top::UsageData::UsageData( const UsageData& older , const UsageData& newer ) {
          // this won't be 100% accurate on rollovers and drop(), but at least it won't be negative
          time  = (newer.time  >= older.time)  ? (newer.time  - older.time)  : newer.time;
          count = (newer.count >= older.count) ? (newer.count - older.count) : newer.count;
      }
  
      Top::CollectionData::CollectionData( const CollectionData& older , const CollectionData& newer )
          : total( older.total , newer.total ) ,
            readLock( older.readLock , newer.readLock ) ,
            writeLock( older.writeLock , newer.writeLock ) ,
            queries( older.queries , newer.queries ) ,
            getmore( older.getmore , newer.getmore ) ,
            insert( older.insert , newer.insert ) ,
            update( older.update , newer.update ) ,
            remove( older.remove , newer.remove ),
            commands( older.commands , newer.commands ) {
  
      }
  
      void Top::record( const string& ns , int op , int lockType , long long micros , bool command ) {
          if ( ns[0] == '?' )
              return;
  
          //cout << "record: " << ns << "\t" << op << "\t" << command << endl;
          scoped_lock lk(_lock);
  
          if ( ( command || op == dbQuery ) && ns == _lastDropped ) {
              _lastDropped = "";
              return;
          }
  
          CollectionData& coll = _usage[ns];
          _record( coll , op , lockType , micros , command );
          _record( _global , op , lockType , micros , command );
      }
  
      void Top::_record( CollectionData& c , int op , int lockType , long long micros , bool command ) {
          c.total.inc( micros );
  
          if ( lockType > 0 )
              c.writeLock.inc( micros );
          else if ( lockType < 0 )
              c.readLock.inc( micros );
  
          switch ( op ) {
          case 0:
              // use 0 for unknown, non-specific
              break;
          case dbUpdate:
              c.update.inc( micros );
              break;
          case dbInsert:
              c.insert.inc( micros );
              break;
          case dbQuery:
              if ( command )
                  c.commands.inc( micros );
              else
                  c.queries.inc( micros );
              break;
          case dbGetMore:
              c.getmore.inc( micros );
              break;
          case dbDelete:
              c.remove.inc( micros );
              break;
          case dbKillCursors:
              break;
          case opReply:
          case dbMsg:
              log() << "unexpected op in Top::record: " << op << endl;
              break;
          default:
              log() << "unknown op in Top::record: " << op << endl;
          }
  
      }
  
      void Top::collectionDropped( const string& ns ) {
          //cout << "collectionDropped: " << ns << endl;
          scoped_lock lk(_lock);
          _usage.erase(ns);
          _lastDropped = ns;
      }
  
      void Top::cloneMap(Top::UsageMap& out) const {
          scoped_lock lk(_lock);
          out = _usage;
      }
  
      void Top::append( BSONObjBuilder& b ) {
          scoped_lock lk( _lock );
          _appendToUsageMap( b , _usage );
      }
  
      void Top::_appendToUsageMap( BSONObjBuilder& b , const UsageMap& map ) const {
          for ( UsageMap::const_iterator i=map.begin(); i!=map.end(); i++ ) {
              BSONObjBuilder bb( b.subobjStart( i->first ) );
  
              const CollectionData& coll = i->second;
  
              _appendStatsEntry( b , "total" , coll.total );
  
              _appendStatsEntry( b , "readLock" , coll.readLock );
              _appendStatsEntry( b , "writeLock" , coll.writeLock );
  
              _appendStatsEntry( b , "queries" , coll.queries );
              _appendStatsEntry( b , "getmore" , coll.getmore );
              _appendStatsEntry( b , "insert" , coll.insert );
              _appendStatsEntry( b , "update" , coll.update );
              _appendStatsEntry( b , "remove" , coll.remove );
              _appendStatsEntry( b , "commands" , coll.commands );
  
              bb.done();
          }
      }
  
      void Top::_appendStatsEntry( BSONObjBuilder& b , const char * statsName , const UsageData& map ) const {
          BSONObjBuilder bb( b.subobjStart( statsName ) );
          bb.appendNumber( "time" , map.time );
          bb.appendNumber( "count" , map.count );
          bb.done();
      }
  
      class TopCmd : public Command {
      public:
          TopCmd() : Command( "top", true ) {}
  
          virtual bool slaveOk() const { return true; }
          virtual bool adminOnly() const { return true; }
          virtual LockType locktype() const { return READ; }
          virtual void help( stringstream& help ) const { help << "usage by collection, in micros "; }
  
          virtual bool run(const string& , BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
              {
                  BSONObjBuilder b( result.subobjStart( "totals" ) );
                  b.append( "note" , "all times in microseconds" );
                  Top::global.append( b );
                  b.done();
              }
              return true;
          }
  
      } topCmd;
  
      Top Top::global;
  
      TopOld::T TopOld::_snapshotStart = TopOld::currentTime();
      TopOld::D TopOld::_snapshotDuration;
      TopOld::UsageMap TopOld::_totalUsage;
      TopOld::UsageMap TopOld::_snapshotA;
      TopOld::UsageMap TopOld::_snapshotB;
      TopOld::UsageMap &TopOld::_snapshot = TopOld::_snapshotA;
      TopOld::UsageMap &TopOld::_nextSnapshot = TopOld::_snapshotB;
      mongo::mutex TopOld::topMutex("topMutex");
  
  
  }
  