// 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 . */ #include "pch.h" #include "top.h" #include "../../util/net/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, int, 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"); }