| /* | 
 |  * Licensed to the Apache Software Foundation (ASF) under one | 
 |  * or more contributor license agreements. See the NOTICE file | 
 |  * distributed with this work for additional information | 
 |  * regarding copyright ownership. The ASF licenses this file | 
 |  * to you under the Apache License, Version 2.0 (the | 
 |  * "License"); you may not use this file except in compliance | 
 |  * with the License. You may obtain a copy of the License at | 
 |  * | 
 |  *   http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, | 
 |  * software distributed under the License is distributed on an | 
 |  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
 |  * KIND, either express or implied. See the License for the | 
 |  * specific language governing permissions and limitations | 
 |  * under the License. | 
 |  */ | 
 |  | 
 | #ifndef _FACEBOOK_THRIFT_SERVER_TCLIENTINFO_H_ | 
 | #define _FACEBOOK_THRIFT_SERVER_TCLIENTINFO_H_ 1 | 
 |  | 
 | // for inet_ntop -- | 
 | #include <arpa/inet.h> | 
 | #include <thrift/server/TServer.h> | 
 | #include <thrift/transport/TSocket.h> | 
 | #include <thrift/concurrency/Mutex.h> | 
 |  | 
 | namespace apache { namespace thrift { namespace server { | 
 |  | 
 | using namespace apache::thrift; | 
 | using namespace apache::thrift::transport; | 
 | using namespace apache::thrift::concurrency; | 
 | using boost::shared_ptr; | 
 | using std::string; | 
 | using std::vector; | 
 |  | 
 | /** | 
 |  * StableVector -- a minimal vector class where growth is automatic and | 
 |  * vector elements never move as the vector grows.  Allocates new space | 
 |  * as needed, but does not copy old values. | 
 |  * | 
 |  * A level vector stores a list of storage vectors containing the actual | 
 |  * elements.  Levels are added as needed, doubling in size each time. | 
 |  * Locking is only done when a level is added.  Access is amortized | 
 |  * constant time. | 
 |  */ | 
 | template <typename T> | 
 | class StableVector { | 
 |   /// The initial allocation as an exponent of 2 | 
 |   static const uint32_t kInitialSizePowOf2 = 10; | 
 |   /// The initial allocation size | 
 |   static const uint32_t kInitialVectorSize = 1 << kInitialSizePowOf2; | 
 |   /// This bound is guaranteed not to be exceeded on 64-bit archs | 
 |   static const int kMaxLevels = 64; | 
 |  | 
 |   /// Values are kept in one or more of these | 
 |   typedef vector<T> Vect; | 
 |   /// One or more value vectors are kept in one of these | 
 |   typedef vector<Vect*> LevelVector; | 
 |  | 
 |   Mutex mutex_; | 
 |   /// current size | 
 |   size_t size_; | 
 |   _Atomic_word vectLvl_; | 
 |   LevelVector vects_; | 
 |  | 
 |  public: | 
 |   /** | 
 |    * Constructor -- initialize the level vector and allocate the | 
 |    * initial storage vector | 
 |    */ | 
 |   StableVector() | 
 |     : size_(0)  | 
 |     , vectLvl_(0) { | 
 |     vects_.reserve(kMaxLevels); | 
 |     Vect* storageVector(new Vect(1 << kInitialSizePowOf2)); | 
 |     vects_.push_back(storageVector); | 
 |   } | 
 |  | 
 |  private: | 
 |   /** | 
 |    * make sure the requested number of storage levels have been allocated. | 
 |    */ | 
 |   void expand(uint32_t level) { | 
 |     // we need the guard to insure that we only allocate once. | 
 |     Guard g(mutex_); | 
 |     while (level > vectLvl_) { | 
 |       Vect* levelVect(new Vect(1 << (vectLvl_ + kInitialSizePowOf2))); | 
 |       vects_.push_back(levelVect); | 
 |       // we need to make sure this is done after levelVect is inserted | 
 |       // (what we want is effectively a memory barrier here). | 
 |       __gnu_cxx::__atomic_add(&vectLvl_, 1); | 
 |     } | 
 |   } | 
 |  | 
 |   /** | 
 |    * Given an index, determine which level and element of that level is | 
 |    * required.  Grows if needed. | 
 |    */ | 
 |   void which(uint32_t n, uint32_t* vno, uint32_t* idx) { | 
 |     if (n >= size_) { | 
 |       size_ = n + 1; | 
 |     } | 
 |     if (n < kInitialVectorSize) { | 
 |       *idx = n; | 
 |       *vno = 0; | 
 |     } else { | 
 |       uint32_t upper = n >> kInitialSizePowOf2; | 
 |       *vno = CHAR_BIT*sizeof(upper) - __builtin_clz(upper); | 
 |       *idx = n - (1 << (*vno + kInitialSizePowOf2 - 1)); | 
 |       if (*vno > vectLvl_) { | 
 |         expand(*vno); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |  public: | 
 |   /** | 
 |    * Given an index, return a reference to that element, perhaps after | 
 |    * allocating additional space. | 
 |    * | 
 |    * @param n a positive integer | 
 |    */ | 
 |   T& operator[](uint32_t n) { | 
 |     uint32_t vno; | 
 |     uint32_t idx; | 
 |     which(n, &vno, &idx); | 
 |     return (*vects_[vno])[idx]; | 
 |   } | 
 |  | 
 |   /** | 
 |    * Return the present size of the vector. | 
 |    */ | 
 |   size_t size() const { return size_; } | 
 | }; | 
 |  | 
 |  | 
 | /** | 
 |  * This class embodies the representation of a single connection during | 
 |  * processing.  We'll keep one of these per file descriptor in TClientInfo. | 
 |  */ | 
 | class TClientInfoConnection { | 
 |  public: | 
 |   const static int kNameLen = 32; | 
 |  | 
 |  private: | 
 |   typedef union IPAddrUnion { | 
 |     sockaddr_in ipv4; | 
 |     sockaddr_in6 ipv6; | 
 |   }; | 
 |  | 
 |   char call_[kNameLen];            ///< The name of the thrift call | 
 |   IPAddrUnion addr_;               ///< The client's IP address | 
 |   timespec time_;                  ///< Time processing started | 
 |   uint64_t ncalls_;                ///< # of calls processed | 
 |  | 
 |  public: | 
 |   /** | 
 |    * Constructor; insure that no client address or thrift call name is | 
 |    * represented. | 
 |    */ | 
 |   TClientInfoConnection(); | 
 |  | 
 |   /** | 
 |    * A connection has been made; record its address.  Since this is the | 
 |    * first we'll know of a connection we start the timer here as well. | 
 |    */ | 
 |   void recordAddr(const sockaddr* addr); | 
 |  | 
 |   /** | 
 |    * Mark the address as empty/unknown. | 
 |    */ | 
 |   void eraseAddr(); | 
 |  | 
 |   /** | 
 |    * Return a string representing the present address, or NULL if none. | 
 |    * Copies the string into the buffer provided. | 
 |    */ | 
 |   const char* getAddr(char* buf, int len) const; | 
 |  | 
 |   /** | 
 |    * A call has been made on this connection; record its name.  Since this is | 
 |    * called for every thrift call processed, we also do our call count here. | 
 |    */  | 
 |   void recordCall(const char* name); | 
 |  | 
 |   /** | 
 |    * Invoked when processing has ended to clear the call name. | 
 |    */ | 
 |   void eraseCall(); | 
 |  | 
 |   /** | 
 |    * Return as string the thrift call either currently being processed or | 
 |    * most recently processed if the connection is still open for additonal | 
 |    * calls.  Returns NULL if a call hasn't been made yet or processing | 
 |    * has ended. | 
 |    */ | 
 |   const char* getCall() const; | 
 |  | 
 |   /** | 
 |    * Get the timespec for the start of this connection (specifically, when | 
 |    * recordAddr() was first called). | 
 |    */ | 
 |   void getTime(timespec* time) const; | 
 |  | 
 |   /** | 
 |    * Return the number of calls made on this connection. | 
 |    */ | 
 |   uint64_t getNCalls() const; | 
 |  | 
 |  private: | 
 |   void initTime(); | 
 | }; | 
 |  | 
 |  | 
 | /** | 
 |  * Store for info about a server's clients -- specifically, the client's IP | 
 |  * address and the call it is executing.  This information is indexed by | 
 |  * socket file descriptor and in the present implementation is updated | 
 |  * asynchronously, so it may only approximate reality. | 
 |  */ | 
 | class TClientInfo { | 
 |  private: | 
 |   StableVector<TClientInfoConnection> info_; | 
 |  | 
 |  public: | 
 |   /** | 
 |    * Return the info object for a given file descriptor.  If "grow" is true | 
 |    * extend the info vector if required (such as for a file descriptor not seen | 
 |    * before).  If "grow" is false and the info vector isn't large enough, | 
 |    * or if "fd" is negative, return NULL. | 
 |    */ | 
 |   TClientInfoConnection* getConnection(int fd, bool grow); | 
 |  | 
 |   size_t size() const; | 
 | }; | 
 |  | 
 | /** | 
 |  * This derivation of TServerEventHandler encapsulates the main status vector | 
 |  * and provides context to the server's processing loop via overrides. | 
 |  * Together with TClientInfoCallHandler (derived from TProcessorEventHandler)  | 
 |  * it integrates client info collection into the server. | 
 |  */ | 
 | class TClientInfoServerHandler : public TServerEventHandler { | 
 |  private: | 
 |   TClientInfo clientInfo_; | 
 |  | 
 |  public: | 
 |   /** | 
 |    * One of these is constructed for each open connection/descriptor and links | 
 |    * to both the status vector (clientInfo_) and that descriptor's entry | 
 |    * within it. | 
 |    */ | 
 |   struct Connect { | 
 |     TClientInfo* clientInfo_; | 
 |     TClientInfoConnection* callInfo_; | 
 |  | 
 |     explicit Connect(TClientInfo* clientInfo) | 
 |       : clientInfo_(clientInfo) | 
 |       , callInfo_(NULL) { | 
 |     } | 
 |   }; | 
 |  | 
 |   /** | 
 |    * Generate processor context; we don't know what descriptor we belong to | 
 |    * yet -- we'll get hooked up in contextProcess().  | 
 |    */ | 
 |   void* createContext(boost::shared_ptr<TProtocol> input, | 
 |                       boost::shared_ptr<TProtocol> output); | 
 |  | 
 |   /** | 
 |    * Mark our slot as unused and delete the context created in createContext(). | 
 |    */ | 
 |   void deleteContext(void* processorContext, | 
 |                      boost::shared_ptr<TProtocol> input, | 
 |                      boost::shared_ptr<TProtocol> output); | 
 |    | 
 |   /** | 
 |    * Called in the processing loop just before the server invokes the | 
 |    * processor itself, on the first call we establish which descriptor | 
 |    * we correspond to and set it to that socket's peer IP address.  This | 
 |    * also has the side effect of initializing call counting and connection | 
 |    * timing.  We won't know which call we're handling until the handler | 
 |    * first gets called in TClientInfoCallHandler::getContext(). | 
 |    */ | 
 |   void processContext(void* processorContext, | 
 |                       shared_ptr<TTransport> transport); | 
 |  | 
 |   /** | 
 |    * Get status report for server in the form of a vector of strings. | 
 |    * Each active client appears as one string in the format: | 
 |    * | 
 |    *     FD IPADDR CALLNAME DURATION NCALLS | 
 |    * | 
 |    * where "FD" is the file descriptor for the client's socket, "IPADDR" | 
 |    * is the IP address (as reported by accept()), "CALLNAME" is the | 
 |    * current or most recent Thrift function name, "DURATION" is the | 
 |    * duration of the connection, while NCALLS is the number of Thrift | 
 |    * calls made since the connection was made.  A single space separates | 
 |    * fields. | 
 |    */ | 
 |   void getStatsStrings(vector<string>& result); | 
 | }; | 
 |  | 
 | /** | 
 |  * This class derives from TProcessorEventHandler to gain access to the | 
 |  * function name for the current Thrift call.  We need two versions of | 
 |  * this -- TClientInfoCallStatsHandler is the other -- since in the latter | 
 |  * case we pass through to TFunctionStatHandler to perform Thrift call | 
 |  * stats. | 
 |  */ | 
 | class TClientInfoCallHandler : public TProcessorEventHandler { | 
 |  public: | 
 |   virtual void* getContext(const char* fn_name, void* serverContext); | 
 | }; | 
 |  | 
 | } } } // namespace apache::thrift::server | 
 |  | 
 | #endif // !_FACEBOOK_THRIFT_SERVER_TCLIENTINFO_H_ |