text: // @file cpuprofile.cpp
  
  /**
  *    Copyright (C) 2012 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/>.
  *
  *    As a special exception, the copyright holders give permission to link the
  *    code of portions of this program with the OpenSSL library under certain
  *    conditions as described in each individual source file and distribute
  *    linked combinations including the program with the OpenSSL library. You
  *    must comply with the GNU Affero General Public License in all respects for
  *    all of the code used other than as permitted herein. If you modify file(s)
  *    with this exception, you may extend this exception to your version of the
  *    file(s), but you are not obligated to do so. If you do not wish to do so,
  *    delete this exception statement from your version. If you delete this
  *    exception statement from all source files in the program, then also delete
  *    it in the license file.
  */
  
  /**
   * This module provides commands for starting and stopping the Google perftools
   * cpu profiler linked into mongod.
   *
   * The following command enables the not-currently-enabled profiler, and writes
   * the profile data to the specified "profileFilename."
   *     { _cpuProfilerStart: { profileFilename: '/path/on/mongod-host.prof' } }
   *
   * The following command disables the already-enabled profiler:
   *     { _cpuProfilerStop: 1}
   *
   * The commands defined here, and profiling, are only available when enabled at
   * build-time with the "--use-cpu-profiler" argument to scons.
   *
   * Example SCons command line: 
   *
   *     scons --release --use-cpu-profiler 
   */
  
  #include "third_party/gperftools-2.2/src/gperftools/profiler.h"
  
  #include <string>
  #include <vector>
  
  #include "mongo/db/auth/action_set.h"
  #include "mongo/db/auth/action_type.h"
  #include "mongo/db/auth/authorization_manager.h"
  #include "mongo/db/auth/privilege.h"
  #include "mongo/db/client.h"
  #include "mongo/db/commands.h"
  #include "mongo/db/jsobj.h"
  
  namespace mongo {
  
      namespace {
  
          /**
           * Common code for the implementation of cpu profiler commands.
           */
          class CpuProfilerCommand : public Command {
          public:
              CpuProfilerCommand( char const *name ) : Command( name ) {}
              virtual bool slaveOk() const { return true; }
              virtual bool adminOnly() const { return true; }
              virtual bool localHostOnlyIfNoAuth( const BSONObj& cmdObj ) { return true; }
              virtual void addRequiredPrivileges(const std::string& dbname,
                                                 const BSONObj& cmdObj,
                                                 std::vector<Privilege>* out) {
                  ActionSet actions;
                  actions.addAction(ActionType::cpuProfiler);
                  out->push_back(Privilege(ResourcePattern::forClusterResource(), actions));
              }
  
              // This is an abuse of the global dbmutex.  We only really need to
              // ensure that only one cpuprofiler command runs at once; it would
              // be fine for it to run concurrently with other operations.
              virtual bool isWriteCommandForConfigServer() const { return true; }
          };
  
          /**
           * Class providing implementation of the _cpuProfilerStart command.
           */
          class CpuProfilerStartCommand : public CpuProfilerCommand {
          public:
              CpuProfilerStartCommand() : CpuProfilerCommand( commandName ) {}
  
              virtual bool run( OperationContext* txn,
                                std::string const &db,
                                BSONObj &cmdObj,
                                int options,
                                std::string &errmsg,
                                BSONObjBuilder &result,
                                bool fromRepl );
  
              static char const *const commandName;
          } cpuProfilerStartCommandInstance;
  
          /**
           * Class providing implementation of the _cpuProfilerStop command.
           */
          class CpuProfilerStopCommand : public CpuProfilerCommand {
          public:
              CpuProfilerStopCommand() : CpuProfilerCommand( commandName ) {}
  
              virtual bool run( OperationContext* txn,
                                std::string const &db,
                                BSONObj &cmdObj,
                                int options,
                                std::string &errmsg,
                                BSONObjBuilder &result,
                                bool fromRepl );
  
              static char const *const commandName;
          } cpuProfilerStopCommandInstance;
  
          char const *const CpuProfilerStartCommand::commandName = "_cpuProfilerStart";
          char const *const CpuProfilerStopCommand::commandName = "_cpuProfilerStop";
  
          bool CpuProfilerStartCommand::run( OperationContext* txn,
                                             std::string const &db,
                                             BSONObj &cmdObj,
                                             int options,
                                             std::string &errmsg,
                                             BSONObjBuilder &result,
                                             bool fromRepl ) {
              Lock::DBLock dbXLock(txn->lockState(), db, MODE_X);
              // The lock here is just to prevent concurrency, nothing will write.
              Client::Context ctx(txn, db);
  
              std::string profileFilename = cmdObj[commandName]["profileFilename"].String();
              if ( ! ::ProfilerStart( profileFilename.c_str() ) ) {
                  errmsg = "Failed to start profiler";
                  return false;
              }
              return true;
          }
  
          bool CpuProfilerStopCommand::run( OperationContext* txn,
                                            std::string const &db,
                                            BSONObj &cmdObj,
                                            int options,
                                            std::string &errmsg,
                                            BSONObjBuilder &result,
                                            bool fromRepl ) {
              Lock::DBLock dbXLock(txn->lockState(), db, MODE_X);
              WriteUnitOfWork wunit(txn);
              Client::Context ctx(txn, db);
  
              ::ProfilerStop();
              wunit.commit();
              return true;
          }
  
      }  // namespace
  
  }  // namespace mongo
  
  