Browse Source

split out PStatClientImpl

David Rose 21 years ago
parent
commit
db8cdc119d

+ 6 - 2
panda/src/pstatclient/Sources.pp

@@ -10,7 +10,9 @@
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx   
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx   
 
 
   #define SOURCES \
   #define SOURCES \
-     config_pstats.h pStatClient.I pStatClient.h pStatClientVersion.I  \
+     config_pstats.h pStatClient.I pStatClient.h \
+     pStatClientImpl.I pStatClientImpl.h \
+     pStatClientVersion.I  \
      pStatClientVersion.h pStatClientControlMessage.h  \
      pStatClientVersion.h pStatClientControlMessage.h  \
      pStatCollector.I pStatCollector.h pStatCollectorDef.h  \
      pStatCollector.I pStatCollector.h pStatCollectorDef.h  \
      pStatFrameData.I pStatFrameData.h pStatProperties.h  \
      pStatFrameData.I pStatFrameData.h pStatProperties.h  \
@@ -18,13 +20,15 @@
      pStatTimer.I pStatTimer.h
      pStatTimer.I pStatTimer.h
 
 
   #define INCLUDED_SOURCES  \
   #define INCLUDED_SOURCES  \
-     config_pstats.cxx pStatClient.cxx pStatClientVersion.cxx  \
+     config_pstats.cxx pStatClient.cxx pStatClientImpl.cxx \
+     pStatClientVersion.cxx  \
      pStatClientControlMessage.cxx pStatCollectorDef.cxx  \
      pStatClientControlMessage.cxx pStatCollectorDef.cxx  \
      pStatFrameData.cxx pStatProperties.cxx  \
      pStatFrameData.cxx pStatProperties.cxx  \
      pStatServerControlMessage.cxx 
      pStatServerControlMessage.cxx 
 
 
   #define INSTALL_HEADERS \
   #define INSTALL_HEADERS \
     config_pstats.h pStatClient.I pStatClient.h \
     config_pstats.h pStatClient.I pStatClient.h \
+    pStatClientImpl.I pStatClientImpl.h \
     pStatClientVersion.I pStatClientVersion.h \
     pStatClientVersion.I pStatClientVersion.h \
     pStatClientControlMessage.h pStatCollector.I pStatCollector.h \
     pStatClientControlMessage.h pStatCollector.I pStatCollector.h \
     pStatCollectorDef.h pStatFrameData.I pStatFrameData.h \
     pStatCollectorDef.h pStatFrameData.I pStatFrameData.h \

+ 134 - 12
panda/src/pstatclient/pStatClient.I

@@ -19,29 +19,29 @@
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClient::set_client_name
 //     Function: PStatClient::set_client_name
-//       Access: Public
+//       Access: Published
 //  Description: Sets the name of the client.  This is reported to the
 //  Description: Sets the name of the client.  This is reported to the
 //               PStatsServer, and will presumably be written in the
 //               PStatsServer, and will presumably be written in the
 //               title bar or something.
 //               title bar or something.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PStatClient::
 INLINE void PStatClient::
 set_client_name(const string &name) {
 set_client_name(const string &name) {
-  _client_name = name;
+  get_impl()->set_client_name(name);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClient::get_client_name
 //     Function: PStatClient::get_client_name
-//       Access: Public
+//       Access: Published
 //  Description: Retrieves the name of the client as set.
 //  Description: Retrieves the name of the client as set.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE string PStatClient::
 INLINE string PStatClient::
 get_client_name() const {
 get_client_name() const {
-  return _client_name;
+  return get_impl()->get_client_name();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClient::set_max_rate
 //     Function: PStatClient::set_max_rate
-//       Access: Public
+//       Access: Published
 //  Description: Controls the number of packets that will be sent to
 //  Description: Controls the number of packets that will be sent to
 //               the server.  Normally, one packet is sent per frame,
 //               the server.  Normally, one packet is sent per frame,
 //               but this can flood the server with more packets than
 //               but this can flood the server with more packets than
@@ -56,24 +56,71 @@ get_client_name() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PStatClient::
 INLINE void PStatClient::
 set_max_rate(float rate) {
 set_max_rate(float rate) {
-  _max_rate = rate;
+  get_impl()->set_max_rate(rate);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClient::get_max_rate
 //     Function: PStatClient::get_max_rate
-//       Access: Public
+//       Access: Published
 //  Description: Returns the maximum number of packets that will be
 //  Description: Returns the maximum number of packets that will be
 //               sent to the server per second, per thread.  See
 //               sent to the server per second, per thread.  See
 //               set_max_rate().
 //               set_max_rate().
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE float PStatClient::
 INLINE float PStatClient::
 get_max_rate() const {
 get_max_rate() const {
-  return _max_rate;
+  return get_impl()->get_max_rate();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PStatClient::connect
+//     Function: PStatClient::get_num_collectors
+//       Access: Published
+//  Description: Returns the total number of collectors the Client
+//               knows about.
+////////////////////////////////////////////////////////////////////
+INLINE int PStatClient::
+get_num_collectors() const {
+  return _collectors.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClient::get_num_threads
+//       Access: Published
+//  Description: Returns the total number of threads the Client
+//               knows about.
+////////////////////////////////////////////////////////////////////
+INLINE int PStatClient::
+get_num_threads() const {
+  return _threads.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClient::get_thread_name
+//       Access: Published
+//  Description: Returns the name of the indicated thread.
+////////////////////////////////////////////////////////////////////
+INLINE string PStatClient::
+get_thread_name(int index) const {
+  nassertr(index >= 0 && index < (int)_threads.size(), string());
+  return _threads[index]._name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClient::get_clock
 //       Access: Published
 //       Access: Published
+//  Description: Returns a reference to the PStatClient's clock
+//               object.  It keeps its own clock, instead of using the
+//               global clock object, so the stats won't get mucked up
+//               if you put the global clock in non-real-time mode or
+//               something.
+////////////////////////////////////////////////////////////////////
+INLINE const ClockObject &PStatClient::
+get_clock() const {
+  return get_impl()->get_clock();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClient::connect
+//       Access: Published, Static
 //  Description: Attempts to establish a connection to the indicated
 //  Description: Attempts to establish a connection to the indicated
 //               PStatServer.  Returns true if successful, false on
 //               PStatServer.  Returns true if successful, false on
 //               failure.
 //               failure.
@@ -85,7 +132,7 @@ connect(const string &hostname, int port) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClient::disconnect
 //     Function: PStatClient::disconnect
-//       Access: Published
+//       Access: Published, Static
 //  Description: Closes the connection previously established.
 //  Description: Closes the connection previously established.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void PStatClient::
 INLINE void PStatClient::
@@ -95,7 +142,7 @@ disconnect() {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClient::is_connected
 //     Function: PStatClient::is_connected
-//       Access: Published
+//       Access: Published, Static
 //  Description: Returns true if the client believes it is connected
 //  Description: Returns true if the client believes it is connected
 //               to a working PStatServer, false otherwise.
 //               to a working PStatServer, false otherwise.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -106,7 +153,7 @@ is_connected() {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClient::resume_after_pause
 //     Function: PStatClient::resume_after_pause
-//       Access: Published
+//       Access: Published, Static
 //  Description: Resumes the PStatClient after the simulation has been
 //  Description: Resumes the PStatClient after the simulation has been
 //               paused for a while.  This allows the stats to
 //               paused for a while.  This allows the stats to
 //               continue exactly where it left off, instead of
 //               continue exactly where it left off, instead of
@@ -116,3 +163,78 @@ INLINE void PStatClient::
 resume_after_pause() {
 resume_after_pause() {
   get_global_pstats()->client_resume_after_pause();
   get_global_pstats()->client_resume_after_pause();
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClient::client_connect
+//       Access: Published
+//  Description: The nonstatic implementation of connect().
+////////////////////////////////////////////////////////////////////
+INLINE bool PStatClient::
+client_connect(string hostname, int port) {
+  client_disconnect();
+  return get_impl()->client_connect(hostname, port);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClient::client_is_connected
+//       Access: Published
+//  Description: The nonstatic implementation of is_connected().
+////////////////////////////////////////////////////////////////////
+INLINE bool PStatClient::
+client_is_connected() const {
+  return has_impl() && _impl->client_is_connected();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClient::client_resume_after_pause
+//       Access: Published
+//  Description: Resumes the PStatClient after the simulation has been
+//               paused for a while.  This allows the stats to
+//               continue exactly where it left off, instead of
+//               leaving a big gap that would represent a chug.
+////////////////////////////////////////////////////////////////////
+INLINE void PStatClient::
+client_resume_after_pause() {
+  if (has_impl()) {
+    _impl->client_resume_after_pause();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClient::has_impl
+//       Access: Private
+//  Description: Returns true if the PStatClientImpl object has been
+//               created for this object yet, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool PStatClient::
+has_impl() const {
+  return (_impl != (PStatClientImpl *)NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClient::get_impl
+//       Access: Private
+//  Description: Returns the PStatClientImpl object for this object.
+//               If the PStatClientImpl object has not yet been
+//               created, implicitly creates it.
+////////////////////////////////////////////////////////////////////
+INLINE PStatClientImpl *PStatClient::
+get_impl() {
+  if (_impl == (PStatClientImpl *)NULL) {
+    _impl = new PStatClientImpl(this);
+  }
+
+  return _impl;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClient::get_impl
+//       Access: Private
+//  Description: Returns the PStatClientImpl object for this object.
+//               If the PStatClientImpl object has not yet been
+//               created, implicitly creates it.
+////////////////////////////////////////////////////////////////////
+INLINE const PStatClientImpl *PStatClient::
+get_impl() const {
+  return ((PStatClient *)this)->get_impl();
+}

+ 115 - 565
panda/src/pstatclient/pStatClient.cxx

@@ -22,29 +22,21 @@
 // This file only defines anything interesting if DO_PSTATS is
 // This file only defines anything interesting if DO_PSTATS is
 // defined.
 // defined.
 
 
+#include "pStatClientImpl.h"
 #include "pStatClientControlMessage.h"
 #include "pStatClientControlMessage.h"
 #include "pStatServerControlMessage.h"
 #include "pStatServerControlMessage.h"
 #include "pStatCollector.h"
 #include "pStatCollector.h"
 #include "pStatThread.h"
 #include "pStatThread.h"
 #include "config_pstats.h"
 #include "config_pstats.h"
 #include "pStatProperties.h"
 #include "pStatProperties.h"
-#include "cmath.h"
-#include "mathNumbers.h"
 
 
-#include <algorithm>
-
-#ifdef WIN32_VC
-#define WINDOWS_LEAN_AND_MEAN
-#include <windows.h>
-#undef WINDOWS_LEAN_AND_MEAN
-#endif
+PStatCollector PStatClient::_total_size_pcollector("Memory usage");
+PStatCollector PStatClient::_cpp_size_pcollector("Memory usage:C++");
+PStatCollector PStatClient::_interpreter_size_pcollector("Memory usage:Interpreter");
+PStatCollector PStatClient::_pstats_pcollector("App:PStats");
 
 
 PStatClient *PStatClient::_global_pstats = NULL;
 PStatClient *PStatClient::_global_pstats = NULL;
 
 
-PStatCollector _total_size_pcollector("Memory usage");
-PStatCollector _cpp_size_pcollector("Memory usage:C++");
-PStatCollector _interpreter_size_pcollector("Memory usage:Interpreter");
-PStatCollector _pstats_pcollector("App:PStats");
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClient::PerThreadData::Constructor
 //     Function: PStatClient::PerThreadData::Constructor
@@ -65,17 +57,8 @@ PerThreadData() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PStatClient::
 PStatClient::
 PStatClient() :
 PStatClient() :
-  _reader(this, 0),
-  _writer(this, get_pstats_threaded_write() ? 1 : 0)
+  _impl(NULL)
 {
 {
-  _is_connected = false;
-  _got_udp_port = false;
-  _collectors_reported = 0;
-  _threads_reported = 0;
-
-  // Make sure our clock is in "normal" mode.
-  _clock.set_mode(ClockObject::M_normal);
-
   // We always have a collector at index 0 named "Frame".  This tracks
   // We always have a collector at index 0 named "Frame".  This tracks
   // the total frame time and is the root of all other collectors.  We
   // the total frame time and is the root of all other collectors.  We
   // have to make this one by hand since it's the root.
   // have to make this one by hand since it's the root.
@@ -87,28 +70,6 @@ PStatClient() :
 
 
   // We also always have a thread at index 0 named "Main".
   // We also always have a thread at index 0 named "Main".
   make_thread("Main");
   make_thread("Main");
-
-  _client_name = get_pstats_name();
-  _max_rate = get_pstats_max_rate();
-
-  _tcp_count = 1;
-  _udp_count = 1;
-
-  double pstats_tcp_ratio = get_pstats_tcp_ratio();
-
-  if (pstats_tcp_ratio >= 1.0f) {
-    _tcp_count_factor = 0.0f;
-    _udp_count_factor = 1.0f;
-
-  } else if (pstats_tcp_ratio <= 0.0f) {
-    _tcp_count_factor = 1.0f;
-    _udp_count_factor = 0.0f;
-
-  } else {
-    csincos(pstats_tcp_ratio * MathNumbers::pi_f / 2.0f,
-            &_udp_count_factor,
-            &_tcp_count_factor);
-  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -119,22 +80,15 @@ PStatClient() :
 PStatClient::
 PStatClient::
 ~PStatClient() {
 ~PStatClient() {
   disconnect();
   disconnect();
-}
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: PStatClient::get_num_collectors
-//       Access: Public
-//  Description: Returns the total number of collectors the Client
-//               knows about.
-////////////////////////////////////////////////////////////////////
-int PStatClient::
-get_num_collectors() const {
-  return _collectors.size();
+  if (has_impl()) {
+    delete _impl;
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClient::get_collector
 //     Function: PStatClient::get_collector
-//       Access: Public
+//       Access: Published
 //  Description: Returns the nth collector.
 //  Description: Returns the nth collector.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PStatCollector PStatClient::
 PStatCollector PStatClient::
@@ -145,7 +99,7 @@ get_collector(int index) const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClient::get_collector_def
 //     Function: PStatClient::get_collector_def
-//       Access: Public
+//       Access: Published
 //  Description: Returns the definition body of the nth collector.
 //  Description: Returns the definition body of the nth collector.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 const PStatCollectorDef &PStatClient::
 const PStatCollectorDef &PStatClient::
@@ -160,7 +114,7 @@ get_collector_def(int index) const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClient::get_collector_name
 //     Function: PStatClient::get_collector_name
-//       Access: Public
+//       Access: Published
 //  Description: Returns the name of the indicated collector.
 //  Description: Returns the name of the indicated collector.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 string PStatClient::
 string PStatClient::
@@ -173,7 +127,7 @@ get_collector_name(int index) const {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClient::get_collector_fullname
 //     Function: PStatClient::get_collector_fullname
-//       Access: Public
+//       Access: Published
 //  Description: Returns the "full name" of the indicated collector.
 //  Description: Returns the "full name" of the indicated collector.
 //               This will be the concatenation of all of the
 //               This will be the concatenation of all of the
 //               collector's parents' names (except Frame) and the
 //               collector's parents' names (except Frame) and the
@@ -191,20 +145,9 @@ get_collector_fullname(int index) const {
   }
   }
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: PStatClient::get_num_threads
-//       Access: Public
-//  Description: Returns the total number of threads the Client
-//               knows about.
-////////////////////////////////////////////////////////////////////
-int PStatClient::
-get_num_threads() const {
-  return _threads.size();
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClient::get_thread
 //     Function: PStatClient::get_thread
-//       Access: Public
+//       Access: Published
 //  Description: Returns the nth thread.
 //  Description: Returns the nth thread.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PStatThread PStatClient::
 PStatThread PStatClient::
@@ -214,44 +157,103 @@ get_thread(int index) const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PStatClient::get_thread_name
-//       Access: Public
-//  Description: Returns the name of the indicated thread.
+//     Function: PStatClient::get_main_thread
+//       Access: Published
+//  Description: Returns a handle to the client's "Main", or default,
+//               thread.  This is where collectors will be started and
+//               stopped if they don't specify otherwise.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-string PStatClient::
-get_thread_name(int index) const {
-  nassertr(index >= 0 && index < (int)_threads.size(), string());
-  return _threads[index]._name;
+PStatThread PStatClient::
+get_main_thread() const {
+  return PStatThread((PStatClient *)this, 0);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PStatClient::get_clock
-//       Access: Public
-//  Description: Returns a reference to the PStatClient's clock
-//               object.  It keeps its own clock, instead of using the
-//               global clock object, so the stats won't get mucked up
-//               if you put the global clock in non-real-time mode or
-//               something.
-//
-//               On second thought, it works better to use the global
-//               clock, so we don't lose a lot of time in the stats
-//               while we're waiting at the prompt.
+//     Function: PStatClient::main_tick
+//       Access: Published, Static
+//  Description: A convenience function to call new_frame() on the
+//               global PStatClient's main thread.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-const ClockObject &PStatClient::
-get_clock() const {
-  return _clock;
+void PStatClient::
+main_tick() {
+  // We have code here to report the memory usage.  We can't put this
+  // code inside the MemoryUsage class, where it fits a little better,
+  // simply because MemoryUsage is a very low-level class that doesn't
+  // know about PStatClient.
+
+#ifdef DO_MEMORY_USAGE
+  if (MemoryUsage::has_total_size()) {
+    _total_size_pcollector.set_level(MemoryUsage::get_total_size());
+  }
+  if (MemoryUsage::has_cpp_size()) {
+    _cpp_size_pcollector.set_level(MemoryUsage::get_cpp_size());
+  }
+  if (MemoryUsage::has_interpreter_size()) {
+    _interpreter_size_pcollector.set_level(MemoryUsage::get_interpreter_size());
+  }
+#endif
+
+  get_global_pstats()->client_main_tick();
+}  
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClient::client_main_tick
+//       Access: Published
+//  Description: A convenience function to call new_frame() on the
+//               the given client's main thread.
+////////////////////////////////////////////////////////////////////
+void PStatClient::
+client_main_tick() {
+  if (has_impl()) {
+    _impl->client_main_tick();
+  }
+  get_main_thread().new_frame();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: PStatClient::get_main_thread
-//       Access: Public
-//  Description: Returns a handle to the client's "Main", or default,
-//               thread.  This is where collectors will be started and
-//               stopped if they don't specify otherwise.
+//     Function: PStatClient::client_disconnect
+//       Access: Published
+//  Description: The nonstatic implementation of disconnect().
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-PStatThread PStatClient::
-get_main_thread() const {
-  return PStatThread((PStatClient *)this, 0);
+void PStatClient::
+client_disconnect() {
+  if (has_impl()) {
+    _impl->client_disconnect();
+  }
+
+  Threads::iterator ti;
+  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
+    (*ti)._frame_number = 0;
+    (*ti)._is_active = false;
+    (*ti)._next_packet = 0.0;
+    (*ti)._frame_data.clear();
+  }
+
+  Collectors::iterator ci;
+  for (ci = _collectors.begin(); ci != _collectors.end(); ++ci) {
+    PerThread::iterator ii;
+    for (ii = (*ci)._per_thread.begin();
+         ii != (*ci)._per_thread.end();
+         ++ii) {
+      (*ii)._nested_count = 0;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClient::get_global_pstats
+//       Access: Published, Static
+//  Description: Returns a pointer to the global PStatClient object.
+//               It's legal to declare your own PStatClient locally,
+//               but it's also convenient to have a global one that
+//               everyone can register with.  This is the global one.
+////////////////////////////////////////////////////////////////////
+PStatClient *PStatClient::
+get_global_pstats() {
+  if (_global_pstats == (PStatClient *)NULL) {
+    _global_pstats = new PStatClient;
+  }
+  return _global_pstats;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -388,175 +390,6 @@ make_thread(const string &name) {
   return PStatThread(this, new_index);
   return PStatThread(this, new_index);
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: PStatClient::main_tick
-//       Access: Public, Static
-//  Description: A convenience function to call new_frame() on the
-//               global PStatClient's main thread.
-////////////////////////////////////////////////////////////////////
-void PStatClient::
-main_tick() {
-  // We have code here to report the memory usage.  We can't put this
-  // code inside the MemoryUsage class, where it fits a little better,
-  // simply because MemoryUsage is a very low-level class that doesn't
-  // know about PStatClient.
-
-#ifdef DO_MEMORY_USAGE
-  if (MemoryUsage::has_total_size()) {
-    _total_size_pcollector.set_level(MemoryUsage::get_total_size());
-  }
-  if (MemoryUsage::has_cpp_size()) {
-    _cpp_size_pcollector.set_level(MemoryUsage::get_cpp_size());
-  }
-  if (MemoryUsage::has_interpreter_size()) {
-    _interpreter_size_pcollector.set_level(MemoryUsage::get_interpreter_size());
-  }
-#endif
-
-  get_global_pstats()->client_main_tick();
-}  
-
-////////////////////////////////////////////////////////////////////
-//     Function: PStatClient::main_tick
-//       Access: Public, Static
-//  Description: A convenience function to call new_frame() on the
-//               the given client's main thread.
-////////////////////////////////////////////////////////////////////
-void PStatClient::
-client_main_tick() {
-  _clock.tick();
-  get_main_thread().new_frame();
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PStatClient::get_global_pstats
-//       Access: Public, Static
-//  Description: Returns a pointer to the global PStatClient object.
-//               It's legal to declare your own PStatClient locally,
-//               but it's also convenient to have a global one that
-//               everyone can register with.  This is the global one.
-////////////////////////////////////////////////////////////////////
-PStatClient *PStatClient::
-get_global_pstats() {
-  if (_global_pstats == (PStatClient *)NULL) {
-    _global_pstats = new PStatClient;
-  }
-  return _global_pstats;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PStatClient::client_connect
-//       Access: Private
-//  Description: The nonstatic implementation of connect().
-////////////////////////////////////////////////////////////////////
-bool PStatClient::
-client_connect(string hostname, int port) {
-  client_disconnect();
-
-  if (hostname.empty()) {
-    hostname = pstats_host;
-  }
-  if (port < 0) {
-    port = pstats_port;
-  }
-
-  if (!_server.set_host(hostname, port)) {
-    pstats_cat.error()
-      << "Unknown host: " << hostname << "\n";
-    return false;
-  }
-
-  _tcp_connection = open_TCP_client_connection(_server, 5000);
-
-  if (_tcp_connection.is_null()) {
-    pstats_cat.error()
-      << "Couldn't connect to PStatServer at " << hostname << ":"
-      << port << "\n";
-    return false;
-  }
-  // Make sure we're not queuing up multiple TCP sockets--we expect
-  // immediate writes of our TCP datagrams.
-  _tcp_connection->set_collect_tcp(false);
-
-  _reader.add_connection(_tcp_connection);
-  _is_connected = true;
-
-  _udp_connection = open_UDP_connection();
-
-  send_hello();
-
-  return _is_connected;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PStatClient::client_disconnect
-//       Access: Private
-//  Description: The nonstatic implementation of disconnect().
-////////////////////////////////////////////////////////////////////
-void PStatClient::
-client_disconnect() {
-  if (_is_connected) {
-    _reader.remove_connection(_tcp_connection);
-    close_connection(_tcp_connection);
-    close_connection(_udp_connection);
-  }
-
-  _tcp_connection.clear();
-  _udp_connection.clear();
-
-  _is_connected = false;
-  _got_udp_port = false;
-
-  _collectors_reported = 0;
-  _threads_reported = 0;
-
-  Threads::iterator ti;
-  for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
-    (*ti)._frame_number = 0;
-    (*ti)._is_active = false;
-    (*ti)._next_packet = 0.0;
-    (*ti)._frame_data.clear();
-  }
-
-  Collectors::iterator ci;
-  for (ci = _collectors.begin(); ci != _collectors.end(); ++ci) {
-    PerThread::iterator ii;
-    for (ii = (*ci)._per_thread.begin();
-         ii != (*ci)._per_thread.end();
-         ++ii) {
-      (*ii)._nested_count = 0;
-    }
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PStatClient::client_is_connected
-//       Access: Public
-//  Description: The nonstatic implementation of is_connected().
-////////////////////////////////////////////////////////////////////
-bool PStatClient::
-client_is_connected() const {
-  return _is_connected;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PStatClient::client_resume_after_pause
-//       Access: Published
-//  Description: Resumes the PStatClient after the simulation has been
-//               paused for a while.  This allows the stats to
-//               continue exactly where it left off, instead of
-//               leaving a big gap that would represent a chug.
-////////////////////////////////////////////////////////////////////
-void PStatClient::
-client_resume_after_pause() {
-  // Simply reset the clock to the beginning of the last frame.  This
-  // may lose a frame, but on the other hand we won't skip a whole
-  // slew of frames either.
-
-  double frame_time = _clock.get_frame_time();
-  _clock.set_real_time(frame_time);
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClient::is_active
 //     Function: PStatClient::is_active
 //       Access: Private
 //       Access: Private
@@ -572,7 +405,7 @@ is_active(int collector_index, int thread_index) const {
   nassertr(collector_index >= 0 && collector_index < (int)_collectors.size(), false);
   nassertr(collector_index >= 0 && collector_index < (int)_collectors.size(), false);
   nassertr(thread_index >= 0 && thread_index < (int)_threads.size(), false);
   nassertr(thread_index >= 0 && thread_index < (int)_threads.size(), false);
 
 
-  return (_is_connected &&
+  return (client_is_connected() &&
           _collectors[collector_index]._def->_is_active &&
           _collectors[collector_index]._def->_is_active &&
           _threads[thread_index]._is_active);
           _threads[thread_index]._is_active);
 }
 }
@@ -610,13 +443,14 @@ start(int collector_index, int thread_index) {
   nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
   nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
 #endif
 #endif
 
 
-  if (_collectors[collector_index]._def->_is_active &&
+  if (client_is_connected() && 
+      _collectors[collector_index]._def->_is_active &&
       _threads[thread_index]._is_active) {
       _threads[thread_index]._is_active) {
     if (_collectors[collector_index]._per_thread[thread_index]._nested_count == 0) {
     if (_collectors[collector_index]._per_thread[thread_index]._nested_count == 0) {
       // This collector wasn't already started in this thread; record
       // This collector wasn't already started in this thread; record
       // a new data point.
       // a new data point.
       _threads[thread_index]._frame_data.add_start(collector_index, 
       _threads[thread_index]._frame_data.add_start(collector_index, 
-                                                   _clock.get_real_time());
+                                                   get_clock().get_real_time());
     }
     }
     _collectors[collector_index]._per_thread[thread_index]._nested_count++;
     _collectors[collector_index]._per_thread[thread_index]._nested_count++;
   }
   }
@@ -636,7 +470,8 @@ start(int collector_index, int thread_index, float as_of) {
   nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
   nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
 #endif
 #endif
 
 
-  if (_collectors[collector_index]._def->_is_active &&
+  if (client_is_connected() && 
+      _collectors[collector_index]._def->_is_active &&
       _threads[thread_index]._is_active) {
       _threads[thread_index]._is_active) {
     if (_collectors[collector_index]._per_thread[thread_index]._nested_count == 0) {
     if (_collectors[collector_index]._per_thread[thread_index]._nested_count == 0) {
       // This collector wasn't already started in this thread; record
       // This collector wasn't already started in this thread; record
@@ -661,7 +496,8 @@ stop(int collector_index, int thread_index) {
   nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
   nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
 #endif
 #endif
 
 
-  if (_collectors[collector_index]._def->_is_active &&
+  if (client_is_connected() && 
+      _collectors[collector_index]._def->_is_active &&
       _threads[thread_index]._is_active) {
       _threads[thread_index]._is_active) {
     if (_collectors[collector_index]._per_thread[thread_index]._nested_count == 0) {
     if (_collectors[collector_index]._per_thread[thread_index]._nested_count == 0) {
       pstats_cat.warning()
       pstats_cat.warning()
@@ -677,7 +513,7 @@ stop(int collector_index, int thread_index) {
       // This collector has now been completely stopped; record a new
       // This collector has now been completely stopped; record a new
       // data point.
       // data point.
       _threads[thread_index]._frame_data.add_stop(collector_index,
       _threads[thread_index]._frame_data.add_stop(collector_index,
-                                                  _clock.get_real_time());
+                                                  get_clock().get_real_time());
     }
     }
   }
   }
 }
 }
@@ -696,7 +532,8 @@ stop(int collector_index, int thread_index, float as_of) {
   nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
   nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
 #endif
 #endif
 
 
-  if (_collectors[collector_index]._def->_is_active &&
+  if (client_is_connected() &&
+      _collectors[collector_index]._def->_is_active &&
       _threads[thread_index]._is_active) {
       _threads[thread_index]._is_active) {
     if (_collectors[collector_index]._per_thread[thread_index]._nested_count == 0) {
     if (_collectors[collector_index]._per_thread[thread_index]._nested_count == 0) {
       pstats_cat.warning()
       pstats_cat.warning()
@@ -745,7 +582,7 @@ clear_level(int collector_index, int thread_index) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PStatClient::
 void PStatClient::
 set_level(int collector_index, int thread_index, float level) {
 set_level(int collector_index, int thread_index, float level) {
-  if (_collectors[collector_index]._def->_is_active) {
+  if (client_is_connected() && _collectors[collector_index]._def->_is_active) {
     level *= _collectors[collector_index]._def->_factor;
     level *= _collectors[collector_index]._def->_factor;
     _collectors[collector_index]._per_thread[thread_index]._has_level = true;
     _collectors[collector_index]._per_thread[thread_index]._has_level = true;
     _collectors[collector_index]._per_thread[thread_index]._level = level;
     _collectors[collector_index]._per_thread[thread_index]._level = level;
@@ -765,7 +602,7 @@ set_level(int collector_index, int thread_index, float level) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PStatClient::
 void PStatClient::
 add_level(int collector_index, int thread_index, float increment) {
 add_level(int collector_index, int thread_index, float increment) {
-  if (_collectors[collector_index]._def->_is_active) {
+  if (client_is_connected() && _collectors[collector_index]._def->_is_active) {
     increment *= _collectors[collector_index]._def->_factor;
     increment *= _collectors[collector_index]._def->_factor;
     _collectors[collector_index]._per_thread[thread_index]._has_level = true;
     _collectors[collector_index]._per_thread[thread_index]._has_level = true;
     _collectors[collector_index]._per_thread[thread_index]._level += increment;
     _collectors[collector_index]._per_thread[thread_index]._level += increment;
@@ -786,291 +623,4 @@ get_level(int collector_index, int thread_index) const {
     _collectors[collector_index]._def->_factor;
     _collectors[collector_index]._def->_factor;
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: PStatClient::new_frame
-//       Access: Private
-//  Description: Called by the PStatThread interface at the beginning
-//               of every frame, for each thread.  This resets the
-//               clocks for the new frame and transmits the data for
-//               the previous frame.
-////////////////////////////////////////////////////////////////////
-void PStatClient::
-new_frame(int thread_index) {
-  nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
-
-  Thread &thread = _threads[thread_index];
-
-  // If we're the main thread, we should exchange control packets with
-  // the server.
-  if (thread_index == 0) {
-    transmit_control_data();
-  }
-
-  // If we've got the UDP port by the time the frame starts, it's
-  // time to become active and start actually tracking data.
-  if (_got_udp_port) {
-    thread._is_active = true;
-  }
-
-  if (!thread._is_active) {
-    return;
-  }
-
-  float frame_start = _clock.get_real_time();
-
-  if (!thread._frame_data.is_empty()) {
-    // Collector 0 is the whole frame.
-    stop(0, thread_index, frame_start);
-
-    // Fill up the level data for all the collectors who have level
-    // data for this thread.
-    int num_collectors = _collectors.size();
-    for (int i = 0; i < num_collectors; i++) {
-      const PerThreadData &ptd = _collectors[i]._per_thread[thread_index];
-      if (ptd._has_level) {
-        thread._frame_data.add_level(i, ptd._level);
-      }
-    }
-    transmit_frame_data(thread_index);
-  }
-
-  thread._frame_data.clear();
-  thread._frame_number++;
-  start(0, thread_index, frame_start);
-
-  // Also record the time for the PStats operation itself.
-  start(_pstats_pcollector.get_index(), thread_index, frame_start);
-  stop(_pstats_pcollector.get_index(), thread_index, _clock.get_real_time());
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PStatClient::transmit_frame_data
-//       Access: Private
-//  Description: Should be called once per frame per thread to
-//               transmit the latest data to the PStatServer.
-////////////////////////////////////////////////////////////////////
-void PStatClient::
-transmit_frame_data(int thread_index) {
-  nassertv(thread_index >= 0 && thread_index < (int)_threads.size());
-  if (_is_connected && _threads[thread_index]._is_active) {
-
-    // We don't want to send too many packets in a hurry and flood the
-    // server.  Check that enough time has elapsed for us to send a
-    // new packet.  If not, we'll drop this packet on the floor and
-    // send a new one next time around.
-    float now = _clock.get_real_time();
-    if (now >= _threads[thread_index]._next_packet) {
-      // We don't want to send more than _max_rate UDP-size packets
-      // per second, per thread.
-      float packet_delay = 1.0 / _max_rate;
-
-      // Send new data.
-      NetDatagram datagram;
-      // We always start with a zero byte, to differentiate it from a
-      // control message.
-      datagram.add_uint8(0);
-
-      datagram.add_uint16(thread_index);
-      datagram.add_uint32(_threads[thread_index]._frame_number);
-      _threads[thread_index]._frame_data.write_datagram(datagram);
-
-      if (_writer.is_valid_for_udp(datagram)) {
-        if (_udp_count * _udp_count_factor < _tcp_count * _tcp_count_factor) {
-          // Send this one as a UDP packet.
-          nassertv(_got_udp_port);
-          _writer.send(datagram, _udp_connection, _server);
-          _udp_count++;
-
-          if (_udp_count == 0) {
-            // Wraparound!
-            _udp_count = 1;
-            _tcp_count = 1;
-          }
-
-        } else {
-          // Send this one as a TCP packet.
-          _writer.send(datagram, _tcp_connection);
-          _tcp_count++;
-
-          if (_tcp_count == 0) {
-            // Wraparound!
-            _udp_count = 1;
-            _tcp_count = 1;
-          }
-        }
-
-      } else {
-        _writer.send(datagram, _tcp_connection);
-        // If our packets are so large that we must ship them via TCP,
-        // then artificially slow down the packet rate even further.
-        int packet_ratio =
-          (datagram.get_length() + maximum_udp_datagram - 1) /
-          maximum_udp_datagram;
-        packet_delay *= (float)packet_ratio;
-      }
-
-      _threads[thread_index]._next_packet = now + packet_delay;
-    }
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PStatClient::transmit_control_data
-//       Access: Private
-//  Description: Should be called once a frame to exchange control
-//               information with the server.
-////////////////////////////////////////////////////////////////////
-void PStatClient::
-transmit_control_data() {
-  // Check for new messages from the server.
-  while (_is_connected && _reader.data_available()) {
-    NetDatagram datagram;
-
-    if (_reader.get_data(datagram)) {
-      PStatServerControlMessage message;
-      if (message.decode(datagram)) {
-        handle_server_control_message(message);
-
-      } else {
-        pstats_cat.error()
-          << "Got unexpected message from server.\n";
-      }
-    }
-  }
-
-  if (_is_connected) {
-    report_new_collectors();
-    report_new_threads();
-  }
-}
-
-
-////////////////////////////////////////////////////////////////////
-//     Function: PStatClient::get_hostname
-//       Access: Private
-//  Description: Returns the current machine's hostname.
-////////////////////////////////////////////////////////////////////
-string PStatClient::
-get_hostname() {
-  if (_hostname.empty()) {
-    char temp_buff[1024];
-    if (gethostname(temp_buff, 1024) == 0) {
-      _hostname = temp_buff;
-    } else {
-      _hostname = "unknown";
-    }
-  }
-  return _hostname;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PStatClient::send_hello
-//       Access: Private
-//  Description: Sends the initial greeting message to the server.
-////////////////////////////////////////////////////////////////////
-void PStatClient::
-send_hello() {
-  nassertv(_is_connected);
-
-  PStatClientControlMessage message;
-  message._type = PStatClientControlMessage::T_hello;
-  message._client_hostname = get_hostname();
-  message._client_progname = _client_name;
-  message._major_version = get_current_pstat_major_version();
-  message._minor_version = get_current_pstat_minor_version();
-
-  Datagram datagram;
-  message.encode(datagram);
-  _writer.send(datagram, _tcp_connection);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PStatClient::report_new_collectors
-//       Access: Private
-//  Description: Sends over any information about new Collectors that
-//               the user code might have recently created.
-////////////////////////////////////////////////////////////////////
-void PStatClient::
-report_new_collectors() {
-  nassertv(_is_connected);
-
-  if (_collectors_reported < (int)_collectors.size()) {
-    PStatClientControlMessage message;
-    message._type = PStatClientControlMessage::T_define_collectors;
-    while (_collectors_reported < (int)_collectors.size()) {
-      message._collectors.push_back(_collectors[_collectors_reported]._def);
-      _collectors_reported++;
-    }
-
-    Datagram datagram;
-    message.encode(datagram);
-    _writer.send(datagram, _tcp_connection);
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PStatClient::report_new_threads
-//       Access: Private
-//  Description: Sends over any information about new Threads that
-//               the user code might have recently created.
-////////////////////////////////////////////////////////////////////
-void PStatClient::
-report_new_threads() {
-  nassertv(_is_connected);
-
-  if (_threads_reported < (int)_threads.size()) {
-    PStatClientControlMessage message;
-    message._type = PStatClientControlMessage::T_define_threads;
-    message._first_thread_index = _threads_reported;
-    while (_threads_reported < (int)_threads.size()) {
-      message._names.push_back(_threads[_threads_reported]._name);
-      _threads_reported++;
-    }
-
-    Datagram datagram;
-    message.encode(datagram);
-    _writer.send(datagram, _tcp_connection);
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PStatClient::handle_server_control_message
-//       Access: Private
-//  Description: Called when a control message has been received by
-//               the server over the TCP connection.
-////////////////////////////////////////////////////////////////////
-void PStatClient::
-handle_server_control_message(const PStatServerControlMessage &message) {
-  switch (message._type) {
-  case PStatServerControlMessage::T_hello:
-    pstats_cat.info()
-      << "Connected to " << message._server_progname << " on "
-      << message._server_hostname << "\n";
-
-    _server.set_port(message._udp_port);
-    _got_udp_port = true;
-    break;
-
-  default:
-    pstats_cat.error()
-      << "Invalid control message received from server.\n";
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: PStatClient::connection_reset
-//       Access: Private, Virtual
-//  Description: Called by the internal net code when the connection
-//               has been lost.
-////////////////////////////////////////////////////////////////////
-void PStatClient::
-connection_reset(const PT(Connection) &connection, PRErrorCode) {
-  if (connection == _tcp_connection) {
-    disconnect();
-  } else {
-    pstats_cat.warning()
-      << "Ignoring spurious connection_reset() message\n";
-  }
-}
-
 #endif // DO_PSTATS
 #endif // DO_PSTATS

+ 23 - 61
panda/src/pstatclient/pStatClient.h

@@ -22,19 +22,10 @@
 #include "pandabase.h"
 #include "pandabase.h"
 
 
 #include "pStatFrameData.h"
 #include "pStatFrameData.h"
-
-#include "clockObject.h"
+#include "pStatClientImpl.h"
 #include "luse.h"
 #include "luse.h"
 #include "pmap.h"
 #include "pmap.h"
 
 
-#ifdef HAVE_NET
-#include "connectionManager.h"
-#include "queuedConnectionReader.h"
-#include "connectionWriter.h"
-#include "netAddress.h"
-#endif
-
-class PStatServerControlMessage;
 class PStatCollector;
 class PStatCollector;
 class PStatCollectorDef;
 class PStatCollectorDef;
 class PStatThread;
 class PStatThread;
@@ -62,25 +53,26 @@ public:
   PStatClient();
   PStatClient();
   ~PStatClient();
   ~PStatClient();
 
 
+PUBLISHED:
   INLINE void set_client_name(const string &name);
   INLINE void set_client_name(const string &name);
   INLINE string get_client_name() const;
   INLINE string get_client_name() const;
   INLINE void set_max_rate(float rate);
   INLINE void set_max_rate(float rate);
   INLINE float get_max_rate() const;
   INLINE float get_max_rate() const;
 
 
-  int get_num_collectors() const;
+  INLINE int get_num_collectors() const;
   PStatCollector get_collector(int index) const;
   PStatCollector get_collector(int index) const;
   const PStatCollectorDef &get_collector_def(int index) const;
   const PStatCollectorDef &get_collector_def(int index) const;
   string get_collector_name(int index) const;
   string get_collector_name(int index) const;
   string get_collector_fullname(int index) const;
   string get_collector_fullname(int index) const;
 
 
-  int get_num_threads() const;
+  INLINE int get_num_threads() const;
   PStatThread get_thread(int index) const;
   PStatThread get_thread(int index) const;
-  string get_thread_name(int index) const;
+  INLINE string get_thread_name(int index) const;
 
 
-  const ClockObject &get_clock() const;
   PStatThread get_main_thread() const;
   PStatThread get_main_thread() const;
 
 
-PUBLISHED:
+  INLINE const ClockObject &get_clock() const;
+
   INLINE static bool connect(const string &hostname = string(), int port = -1);
   INLINE static bool connect(const string &hostname = string(), int port = -1);
   INLINE static void disconnect();
   INLINE static void disconnect();
   INLINE static bool is_connected();
   INLINE static bool is_connected();
@@ -89,17 +81,20 @@ PUBLISHED:
 
 
   static void main_tick();
   static void main_tick();
 
 
-public:
-  static PStatClient *get_global_pstats();
-
   void client_main_tick();
   void client_main_tick();
-  bool client_connect(string hostname, int port);
+  INLINE bool client_connect(string hostname, int port);
   void client_disconnect();
   void client_disconnect();
-  bool client_is_connected() const;
+  INLINE bool client_is_connected() const;
 
 
-  void client_resume_after_pause();
+  INLINE void client_resume_after_pause();
+
+  static PStatClient *get_global_pstats();
 
 
 private:
 private:
+  INLINE bool has_impl() const;
+  INLINE PStatClientImpl *get_impl();
+  INLINE const PStatClientImpl *get_impl() const;
+
   PStatCollector make_collector_with_relname(int parent_index, string relname);
   PStatCollector make_collector_with_relname(int parent_index, string relname);
   PStatCollector make_collector_with_name(int parent_index, const string &name);
   PStatCollector make_collector_with_name(int parent_index, const string &name);
   PStatThread make_thread(const string &name);
   PStatThread make_thread(const string &name);
@@ -117,14 +112,6 @@ private:
   void add_level(int collector_index, int thread_index, float increment);
   void add_level(int collector_index, int thread_index, float increment);
   float get_level(int collector_index, int thread_index) const;
   float get_level(int collector_index, int thread_index) const;
 
 
-  void new_frame(int thread_index);
-  void transmit_frame_data(int thread_index);
-
-  void transmit_control_data();
-
-  // Stats collecting stuff
-  ClockObject _clock;
-
   // Not a phash_map, so the threads remain sorted by name.
   // Not a phash_map, so the threads remain sorted by name.
   typedef pmap<string, int> ThingsByName;
   typedef pmap<string, int> ThingsByName;
   ThingsByName _threads_by_name;
   ThingsByName _threads_by_name;
@@ -167,43 +154,18 @@ private:
   typedef pvector<Thread> Threads;
   typedef pvector<Thread> Threads;
   Threads _threads;
   Threads _threads;
 
 
+  PStatClientImpl *_impl;
 
 
-private:
-  // Networking stuff
-  string get_hostname();
-  void send_hello();
-  void report_new_collectors();
-  void report_new_threads();
-  void handle_server_control_message(const PStatServerControlMessage &message);
-
-  virtual void connection_reset(const PT(Connection) &connection, 
-                                PRErrorCode errcode);
-
-  bool _is_connected;
-  bool _got_udp_port;
-
-  NetAddress _server;
-  QueuedConnectionReader _reader;
-  ConnectionWriter _writer;
-
-  PT(Connection) _tcp_connection;
-  PT(Connection) _udp_connection;
-
-  int _collectors_reported;
-  int _threads_reported;
-
-  string _hostname;
-  string _client_name;
-  float _max_rate;
-
-  float _tcp_count_factor;
-  float _udp_count_factor;
-  unsigned int _tcp_count;
-  unsigned int _udp_count;
+  static PStatCollector _total_size_pcollector;
+  static PStatCollector _cpp_size_pcollector;
+  static PStatCollector _interpreter_size_pcollector;
+  static PStatCollector _pstats_pcollector;
 
 
   static PStatClient *_global_pstats;
   static PStatClient *_global_pstats;
+
   friend class PStatCollector;
   friend class PStatCollector;
   friend class PStatThread;
   friend class PStatThread;
+  friend class PStatClientImpl;
 };
 };
 
 
 #include "pStatClient.I"
 #include "pStatClient.I"

+ 121 - 0
panda/src/pstatclient/pStatClientImpl.I

@@ -0,0 +1,121 @@
+// Filename: pStatClientImpl.I
+// Created by:  drose (23Dec04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientImpl::set_client_name
+//       Access: Public
+//  Description: Sets the name of the client.  This is reported to the
+//               PStatsServer, and will presumably be written in the
+//               title bar or something.
+////////////////////////////////////////////////////////////////////
+INLINE void PStatClientImpl::
+set_client_name(const string &name) {
+  _client_name = name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientImpl::get_client_name
+//       Access: Public
+//  Description: Retrieves the name of the client as set.
+////////////////////////////////////////////////////////////////////
+INLINE string PStatClientImpl::
+get_client_name() const {
+  return _client_name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientImpl::set_max_rate
+//       Access: Public
+//  Description: Controls the number of packets that will be sent to
+//               the server.  Normally, one packet is sent per frame,
+//               but this can flood the server with more packets than
+//               it can handle if the frame rate is especially good
+//               (e.g. if nothing is onscreen at the moment).  Set
+//               this parameter to a reasonable number to prevent this
+//               from happening.
+//
+//               This number specifies the maximum number of packets
+//               that will be sent to the server per second, per
+//               thread.
+////////////////////////////////////////////////////////////////////
+INLINE void PStatClientImpl::
+set_max_rate(float rate) {
+  _max_rate = rate;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientImpl::get_max_rate
+//       Access: Public
+//  Description: Returns the maximum number of packets that will be
+//               sent to the server per second, per thread.  See
+//               set_max_rate().
+////////////////////////////////////////////////////////////////////
+INLINE float PStatClientImpl::
+get_max_rate() const {
+  return _max_rate;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientImpl::get_clock
+//       Access: Public
+//  Description: Returns a reference to the PStatClientImpl's clock
+//               object.  It keeps its own clock, instead of using the
+//               global clock object, so the stats won't get mucked up
+//               if you put the global clock in non-real-time mode or
+//               something.
+////////////////////////////////////////////////////////////////////
+INLINE const ClockObject &PStatClientImpl::
+get_clock() const {
+  return _clock;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientImpl::client_main_tick
+//       Access: Public
+//  Description: Called only by PStatClient::client_main_tick().
+////////////////////////////////////////////////////////////////////
+INLINE void PStatClientImpl::
+client_main_tick() {
+  _clock.tick();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientImpl::client_is_connected
+//       Access: Public
+//  Description: Called only by PStatClient::client_is_connected().
+////////////////////////////////////////////////////////////////////
+INLINE bool PStatClientImpl::
+client_is_connected() const {
+  return _is_connected;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientImpl::client_resume_after_pause
+//       Access: Public
+//  Description: Called only by PStatClient::client_resume_after_pause().
+////////////////////////////////////////////////////////////////////
+INLINE void PStatClientImpl::
+client_resume_after_pause() {
+  // Simply reset the clock to the beginning of the last frame.  This
+  // may lose a frame, but on the other hand we won't skip a whole
+  // slew of frames either.
+
+  double frame_time = _clock.get_frame_time();
+  _clock.set_real_time(frame_time);
+}

+ 448 - 0
panda/src/pstatclient/pStatClientImpl.cxx

@@ -0,0 +1,448 @@
+// Filename: pStatClientImpl.cxx
+// Created by:  drose (23Dec04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "pStatClientImpl.h"
+
+// This file only defines anything if DO_PSTATS is defined.
+#ifdef DO_PSTATS
+
+#include "pStatClient.h"
+#include "pStatClientControlMessage.h"
+#include "pStatServerControlMessage.h"
+#include "pStatCollector.h"
+#include "pStatThread.h"
+#include "config_pstats.h"
+#include "pStatProperties.h"
+#include "cmath.h"
+#include "mathNumbers.h"
+
+#include <algorithm>
+
+#ifdef WIN32_VC
+#include <windows.h>
+#endif
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientImpl::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+PStatClientImpl::
+PStatClientImpl(PStatClient *client) :
+  _client(client),
+  _reader(this, 0),
+  _writer(this, get_pstats_threaded_write() ? 1 : 0)
+{
+  _is_connected = false;
+  _got_udp_port = false;
+  _collectors_reported = 0;
+  _threads_reported = 0;
+
+  // Make sure our clock is in "normal" mode.
+  _clock.set_mode(ClockObject::M_normal);
+
+  _client_name = get_pstats_name();
+  _max_rate = get_pstats_max_rate();
+
+  _tcp_count = 1;
+  _udp_count = 1;
+
+  double pstats_tcp_ratio = get_pstats_tcp_ratio();
+
+  if (pstats_tcp_ratio >= 1.0f) {
+    _tcp_count_factor = 0.0f;
+    _udp_count_factor = 1.0f;
+
+  } else if (pstats_tcp_ratio <= 0.0f) {
+    _tcp_count_factor = 1.0f;
+    _udp_count_factor = 0.0f;
+
+  } else {
+    csincos(pstats_tcp_ratio * MathNumbers::pi_f / 2.0f,
+            &_udp_count_factor,
+            &_tcp_count_factor);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientImpl::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+PStatClientImpl::
+~PStatClientImpl() {
+  nassertv(!_is_connected);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientImpl::client_connect
+//       Access: Public
+//  Description: Called only by PStatClient::client_connect().
+////////////////////////////////////////////////////////////////////
+bool PStatClientImpl::
+client_connect(string hostname, int port) {
+  nassertr(!_is_connected, true);
+
+  if (hostname.empty()) {
+    hostname = pstats_host;
+  }
+  if (port < 0) {
+    port = pstats_port;
+  }
+
+  if (!_server.set_host(hostname, port)) {
+    pstats_cat.error()
+      << "Unknown host: " << hostname << "\n";
+    return false;
+  }
+
+  _tcp_connection = open_TCP_client_connection(_server, 5000);
+
+  if (_tcp_connection.is_null()) {
+    pstats_cat.error()
+      << "Couldn't connect to PStatServer at " << hostname << ":"
+      << port << "\n";
+    return false;
+  }
+  // Make sure we're not queuing up multiple TCP sockets--we expect
+  // immediate writes of our TCP datagrams.
+  _tcp_connection->set_collect_tcp(false);
+
+  _reader.add_connection(_tcp_connection);
+  _is_connected = true;
+
+  _udp_connection = open_UDP_connection();
+
+  send_hello();
+
+  return _is_connected;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientImpl::client_disconnect
+//       Access: Public
+//  Description: Called only by PStatClient::client_disconnect().
+////////////////////////////////////////////////////////////////////
+void PStatClientImpl::
+client_disconnect() {
+  if (_is_connected) {
+    _reader.remove_connection(_tcp_connection);
+    close_connection(_tcp_connection);
+    close_connection(_udp_connection);
+  }
+
+  _tcp_connection.clear();
+  _udp_connection.clear();
+
+  _is_connected = false;
+  _got_udp_port = false;
+
+  _collectors_reported = 0;
+  _threads_reported = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientImpl::new_frame
+//       Access: Public
+//  Description: Called by the PStatThread interface at the beginning
+//               of every frame, for each thread.  This resets the
+//               clocks for the new frame and transmits the data for
+//               the previous frame.
+////////////////////////////////////////////////////////////////////
+void PStatClientImpl::
+new_frame(int thread_index) {
+  nassertv(thread_index >= 0 && thread_index < (int)_client->_threads.size());
+
+  PStatClient::Thread &thread = _client->_threads[thread_index];
+
+  // If we're the main thread, we should exchange control packets with
+  // the server.
+  if (thread_index == 0) {
+    transmit_control_data();
+  }
+
+  // If we've got the UDP port by the time the frame starts, it's
+  // time to become active and start actually tracking data.
+  if (_got_udp_port) {
+    thread._is_active = true;
+  }
+
+  if (!thread._is_active) {
+    return;
+  }
+
+  float frame_start = _clock.get_real_time();
+
+  if (!thread._frame_data.is_empty()) {
+    // Collector 0 is the whole frame.
+    _client->stop(0, thread_index, frame_start);
+
+    // Fill up the level data for all the collectors who have level
+    // data for this thread.
+    int num_collectors = _client->_collectors.size();
+    for (int i = 0; i < num_collectors; i++) {
+      const PStatClient::PerThreadData &ptd = 
+        _client->_collectors[i]._per_thread[thread_index];
+      if (ptd._has_level) {
+        thread._frame_data.add_level(i, ptd._level);
+      }
+    }
+    transmit_frame_data(thread_index);
+  }
+
+  thread._frame_data.clear();
+  thread._frame_number++;
+  _client->start(0, thread_index, frame_start);
+
+  // Also record the time for the PStats operation itself.
+  int pstats_index = PStatClient::_pstats_pcollector.get_index();
+  _client->start(pstats_index, thread_index, frame_start);
+  _client->stop(pstats_index, thread_index, _clock.get_real_time());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientImpl::transmit_frame_data
+//       Access: Private
+//  Description: Should be called once per frame per thread to
+//               transmit the latest data to the PStatServer.
+////////////////////////////////////////////////////////////////////
+void PStatClientImpl::
+transmit_frame_data(int thread_index) {
+  nassertv(thread_index >= 0 && thread_index < (int)_client->_threads.size());
+  if (_is_connected && _client->_threads[thread_index]._is_active) {
+
+    // We don't want to send too many packets in a hurry and flood the
+    // server.  Check that enough time has elapsed for us to send a
+    // new packet.  If not, we'll drop this packet on the floor and
+    // send a new one next time around.
+    float now = _clock.get_real_time();
+    if (now >= _client->_threads[thread_index]._next_packet) {
+      // We don't want to send more than _max_rate UDP-size packets
+      // per second, per thread.
+      float packet_delay = 1.0 / _max_rate;
+
+      // Send new data.
+      NetDatagram datagram;
+      // We always start with a zero byte, to differentiate it from a
+      // control message.
+      datagram.add_uint8(0);
+
+      datagram.add_uint16(thread_index);
+      datagram.add_uint32(_client->_threads[thread_index]._frame_number);
+      _client->_threads[thread_index]._frame_data.write_datagram(datagram);
+
+      if (_writer.is_valid_for_udp(datagram)) {
+        if (_udp_count * _udp_count_factor < _tcp_count * _tcp_count_factor) {
+          // Send this one as a UDP packet.
+          nassertv(_got_udp_port);
+          _writer.send(datagram, _udp_connection, _server);
+          _udp_count++;
+
+          if (_udp_count == 0) {
+            // Wraparound!
+            _udp_count = 1;
+            _tcp_count = 1;
+          }
+
+        } else {
+          // Send this one as a TCP packet.
+          _writer.send(datagram, _tcp_connection);
+          _tcp_count++;
+
+          if (_tcp_count == 0) {
+            // Wraparound!
+            _udp_count = 1;
+            _tcp_count = 1;
+          }
+        }
+
+      } else {
+        _writer.send(datagram, _tcp_connection);
+        // If our packets are so large that we must ship them via TCP,
+        // then artificially slow down the packet rate even further.
+        int packet_ratio =
+          (datagram.get_length() + maximum_udp_datagram - 1) /
+          maximum_udp_datagram;
+        packet_delay *= (float)packet_ratio;
+      }
+
+      _client->_threads[thread_index]._next_packet = now + packet_delay;
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientImpl::transmit_control_data
+//       Access: Private
+//  Description: Should be called once a frame to exchange control
+//               information with the server.
+////////////////////////////////////////////////////////////////////
+void PStatClientImpl::
+transmit_control_data() {
+  // Check for new messages from the server.
+  while (_is_connected && _reader.data_available()) {
+    NetDatagram datagram;
+
+    if (_reader.get_data(datagram)) {
+      PStatServerControlMessage message;
+      if (message.decode(datagram)) {
+        handle_server_control_message(message);
+
+      } else {
+        pstats_cat.error()
+          << "Got unexpected message from server.\n";
+      }
+    }
+  }
+
+  if (_is_connected) {
+    report_new_collectors();
+    report_new_threads();
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientImpl::get_hostname
+//       Access: Private
+//  Description: Returns the current machine's hostname.
+////////////////////////////////////////////////////////////////////
+string PStatClientImpl::
+get_hostname() {
+  if (_hostname.empty()) {
+    char temp_buff[1024];
+    if (gethostname(temp_buff, 1024) == 0) {
+      _hostname = temp_buff;
+    } else {
+      _hostname = "unknown";
+    }
+  }
+  return _hostname;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientImpl::send_hello
+//       Access: Private
+//  Description: Sends the initial greeting message to the server.
+////////////////////////////////////////////////////////////////////
+void PStatClientImpl::
+send_hello() {
+  nassertv(_is_connected);
+
+  PStatClientControlMessage message;
+  message._type = PStatClientControlMessage::T_hello;
+  message._client_hostname = get_hostname();
+  message._client_progname = _client_name;
+  message._major_version = get_current_pstat_major_version();
+  message._minor_version = get_current_pstat_minor_version();
+
+  Datagram datagram;
+  message.encode(datagram);
+  _writer.send(datagram, _tcp_connection);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientImpl::report_new_collectors
+//       Access: Private
+//  Description: Sends over any information about new Collectors that
+//               the user code might have recently created.
+////////////////////////////////////////////////////////////////////
+void PStatClientImpl::
+report_new_collectors() {
+  nassertv(_is_connected);
+
+  if (_collectors_reported < (int)_client->_collectors.size()) {
+    PStatClientControlMessage message;
+    message._type = PStatClientControlMessage::T_define_collectors;
+    while (_collectors_reported < (int)_client->_collectors.size()) {
+      message._collectors.push_back(_client->_collectors[_collectors_reported]._def);
+      _collectors_reported++;
+    }
+
+    Datagram datagram;
+    message.encode(datagram);
+    _writer.send(datagram, _tcp_connection);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientImpl::report_new_threads
+//       Access: Private
+//  Description: Sends over any information about new Threads that
+//               the user code might have recently created.
+////////////////////////////////////////////////////////////////////
+void PStatClientImpl::
+report_new_threads() {
+  nassertv(_is_connected);
+
+  if (_threads_reported < (int)_client->_threads.size()) {
+    PStatClientControlMessage message;
+    message._type = PStatClientControlMessage::T_define_threads;
+    message._first_thread_index = _threads_reported;
+    while (_threads_reported < (int)_client->_threads.size()) {
+      message._names.push_back(_client->_threads[_threads_reported]._name);
+      _threads_reported++;
+    }
+
+    Datagram datagram;
+    message.encode(datagram);
+    _writer.send(datagram, _tcp_connection);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientImpl::handle_server_control_message
+//       Access: Private
+//  Description: Called when a control message has been received by
+//               the server over the TCP connection.
+////////////////////////////////////////////////////////////////////
+void PStatClientImpl::
+handle_server_control_message(const PStatServerControlMessage &message) {
+  switch (message._type) {
+  case PStatServerControlMessage::T_hello:
+    pstats_cat.info()
+      << "Connected to " << message._server_progname << " on "
+      << message._server_hostname << "\n";
+
+    _server.set_port(message._udp_port);
+    _got_udp_port = true;
+    break;
+
+  default:
+    pstats_cat.error()
+      << "Invalid control message received from server.\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClientImpl::connection_reset
+//       Access: Private, Virtual
+//  Description: Called by the internal net code when the connection
+//               has been lost.
+////////////////////////////////////////////////////////////////////
+void PStatClientImpl::
+connection_reset(const PT(Connection) &connection, PRErrorCode) {
+  if (connection == _tcp_connection) {
+    _client->client_disconnect();
+  } else {
+    pstats_cat.warning()
+      << "Ignoring spurious connection_reset() message\n";
+  }
+}
+
+#endif // DO_PSTATS

+ 129 - 0
panda/src/pstatclient/pStatClientImpl.h

@@ -0,0 +1,129 @@
+// Filename: pStatClientImpl.h
+// Created by:  drose (23Dec04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef PSTATCLIENTIMPL_H
+#define PSTATCLIENTIMPL_H
+
+#include "pandabase.h"
+
+// This class doesn't exist at all unless DO_PSTATS is defined.
+#ifdef DO_PSTATS
+
+#include "pStatFrameData.h"
+#include "connectionManager.h"
+#include "queuedConnectionReader.h"
+#include "connectionWriter.h"
+#include "netAddress.h"
+
+#include "clockObject.h"
+#include "luse.h"
+#include "pmap.h"
+
+class PStatClient;
+class PStatServerControlMessage;
+class PStatCollector;
+class PStatCollectorDef;
+class PStatThread;
+
+////////////////////////////////////////////////////////////////////
+//       Class : PStatClientImpl
+// Description : This class is the implementation of the actual
+//               PStatClient class (which is just for interface).  All
+//               of the stuff to manage sending stats up to the server
+//               is handled by this class.
+//
+//               This separation between PStatClient and
+//               PStatClientImpl allows the global PStatClient to be
+//               constructed at static init time, without having to
+//               consult any config variables at that time.  We don't
+//               actually do any real work until someone explicitly
+//               calls PStatClient::connect().
+//
+//               This class doesn't exist at all unless DO_PSTATS is
+//               defined.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA PStatClientImpl : public ConnectionManager {
+public:
+  PStatClientImpl(PStatClient *client);
+  ~PStatClientImpl();
+
+  INLINE void set_client_name(const string &name);
+  INLINE string get_client_name() const;
+  INLINE void set_max_rate(float rate);
+  INLINE float get_max_rate() const;
+
+  INLINE const ClockObject &get_clock() const;
+
+  INLINE void client_main_tick();
+  bool client_connect(string hostname, int port);
+  void client_disconnect();
+  INLINE bool client_is_connected() const;
+
+  INLINE void client_resume_after_pause();
+
+  void new_frame(int thread_index);
+
+private:
+  void transmit_frame_data(int thread_index);
+
+  void transmit_control_data();
+
+  // Stats collecting stuff
+  ClockObject _clock;
+
+  // Networking stuff
+  string get_hostname();
+  void send_hello();
+  void report_new_collectors();
+  void report_new_threads();
+  void handle_server_control_message(const PStatServerControlMessage &message);
+
+  virtual void connection_reset(const PT(Connection) &connection, 
+                                PRErrorCode errcode);
+
+  PStatClient *_client;
+
+  bool _is_connected;
+  bool _got_udp_port;
+
+  NetAddress _server;
+  QueuedConnectionReader _reader;
+  ConnectionWriter _writer;
+
+  PT(Connection) _tcp_connection;
+  PT(Connection) _udp_connection;
+
+  int _collectors_reported;
+  int _threads_reported;
+
+  string _hostname;
+  string _client_name;
+  float _max_rate;
+
+  float _tcp_count_factor;
+  float _udp_count_factor;
+  unsigned int _tcp_count;
+  unsigned int _udp_count;
+};
+
+#include "pStatClientImpl.I"
+
+#endif  // DO_PSTATS
+
+#endif
+

+ 1 - 1
panda/src/pstatclient/pStatThread.I

@@ -99,6 +99,6 @@ operator = (const PStatThread &copy) {
 INLINE void PStatThread::
 INLINE void PStatThread::
 new_frame() {
 new_frame() {
 #ifdef DO_PSTATS
 #ifdef DO_PSTATS
-  _client->new_frame(_index);
+  _client->get_impl()->new_frame(_index);
 #endif
 #endif
 }
 }

+ 1 - 0
panda/src/pstatclient/pstatclient_composite1.cxx

@@ -1,5 +1,6 @@
 
 
 #include "config_pstats.cxx"
 #include "config_pstats.cxx"
 #include "pStatClient.cxx"
 #include "pStatClient.cxx"
+#include "pStatClientImpl.cxx"
 #include "pStatClientVersion.cxx"
 #include "pStatClientVersion.cxx"
 #include "pStatClientControlMessage.cxx"
 #include "pStatClientControlMessage.cxx"