| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498 |
- /**
- * PANDA 3D SOFTWARE
- * Copyright (c) Carnegie Mellon University. All rights reserved.
- *
- * All use of this software is subject to the terms of the revised BSD
- * license. You should have received a copy of this license along
- * with this source code in a file named "LICENSE."
- *
- * @file pStatClientImpl.cxx
- * @author drose
- * @date 2004-12-23
- */
- #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_pstatclient.h"
- #include "pStatProperties.h"
- #include "cmath.h"
- #include <algorithm>
- #if defined(WIN32_VC) || defined(WIN64_VC)
- #include <Winsock2.h>
- #include <windows.h>
- #endif
- /**
- *
- */
- PStatClientImpl::
- PStatClientImpl(PStatClient *client) :
- _clock(TrueClock::get_global_ptr()),
- _delta(0.0),
- _last_frame(0.0),
- _client(client),
- _reader(this, 0),
- _writer(this, pstats_threaded_write ? 1 : 0)
- {
- _writer.set_max_queue_size(pstats_max_queue_size);
- _reader.set_tcp_header_size(4);
- _writer.set_tcp_header_size(4);
- _is_connected = false;
- _got_udp_port = false;
- _collectors_reported = 0;
- _threads_reported = 0;
- _client_name = pstats_name;
- _max_rate = pstats_max_rate;
- _tcp_count = 1;
- _udp_count = 1;
- 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 * (3.14159265f / 2.0f),
- &_udp_count_factor,
- &_tcp_count_factor);
- }
- }
- /**
- *
- */
- PStatClientImpl::
- ~PStatClientImpl() {
- nassertv(!_is_connected);
- }
- /**
- * Called only by PStatClient::client_connect().
- */
- bool PStatClientImpl::
- client_connect(std::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();
- #ifdef DEBUG_THREADS
- MutexDebug::increment_pstats();
- #endif // DEBUG_THREADS
- return _is_connected;
- }
- /**
- * Called only by PStatClient::client_disconnect().
- */
- void PStatClientImpl::
- client_disconnect() {
- if (_is_connected) {
- #ifdef DEBUG_THREADS
- MutexDebug::decrement_pstats();
- #endif // DEBUG_THREADS
- _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;
- }
- /**
- * 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 < _client->_num_threads);
- PStatClient::InternalThread *pthread = _client->get_thread_ptr(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) {
- pthread->_is_active = true;
- }
- if (!pthread->_is_active) {
- return;
- }
- double frame_start = get_real_time();
- int frame_number = -1;
- PStatFrameData frame_data;
- if (!pthread->_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 pthread.
- int num_collectors = _client->_num_collectors;
- PStatClient::CollectorPointer *collectors =
- (PStatClient::CollectorPointer *)_client->_collectors;
- for (int i = 0; i < num_collectors; i++) {
- const PStatClient::PerThreadData &ptd =
- collectors[i]->_per_thread[thread_index];
- if (ptd._has_level) {
- pthread->_frame_data.add_level(i, ptd._level);
- }
- }
- pthread->_frame_data.swap(frame_data);
- frame_number = pthread->_frame_number;
- }
- pthread->_frame_data.clear();
- pthread->_frame_number++;
- _client->start(0, thread_index, frame_start);
- // Also record the time for the PStats operation itself.
- int current_thread_index = Thread::get_current_thread()->get_pstats_index();
- int pstats_index = PStatClient::_pstats_pcollector.get_index();
- _client->start(pstats_index, current_thread_index, frame_start);
- if (frame_number != -1) {
- transmit_frame_data(thread_index, frame_number, frame_data);
- }
- _client->stop(pstats_index, current_thread_index, get_real_time());
- }
- /**
- * Slightly lower-level interface than new_frame that takes a set of frame
- * data.
- */
- void PStatClientImpl::
- add_frame(int thread_index, const PStatFrameData &frame_data) {
- nassertv(thread_index >= 0 && thread_index < _client->_num_threads);
- PStatClient::InternalThread *pthread = _client->get_thread_ptr(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) {
- pthread->_is_active = true;
- }
- if (!pthread->_is_active) {
- return;
- }
- int frame_number = pthread->_frame_number++;
- // Also record the time for the PStats operation itself.
- int current_thread_index = Thread::get_current_thread()->get_pstats_index();
- int pstats_index = PStatClient::_pstats_pcollector.get_index();
- _client->start(pstats_index, current_thread_index);
- if (frame_number != -1) {
- transmit_frame_data(thread_index, frame_number, frame_data);
- }
- _client->stop(pstats_index, current_thread_index);
- }
- /**
- * Should be called once per frame per thread to transmit the latest data to
- * the PStatServer.
- */
- void PStatClientImpl::
- transmit_frame_data(int thread_index, int frame_number,
- const PStatFrameData &frame_data) {
- nassertv(thread_index >= 0 && thread_index < _client->_num_threads);
- PStatClient::InternalThread *thread = _client->get_thread_ptr(thread_index);
- if (_is_connected && thread->_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.
- double now = get_real_time();
- if (now >= thread->_next_packet) {
- // We don't want to send more than _max_rate UDP-size packets per
- // second, per thread.
- double 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(frame_number);
- bool sent;
- if (!frame_data.write_datagram(datagram, _client)) {
- // Too many events to fit in a single datagram. Maybe it was a long
- // frame load or something. Just drop the datagram.
- sent = false;
- } else 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);
- sent = _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.
- sent = _writer.send(datagram, _tcp_connection);
- _tcp_count++;
- if (_tcp_count == 0) {
- // Wraparound!
- _udp_count = 1;
- _tcp_count = 1;
- }
- }
- } else {
- sent = _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 *= (double)packet_ratio;
- }
- thread->_next_packet = now + packet_delay;
- if (!sent) {
- if (pstats_cat.is_debug()) {
- pstats_cat.debug()
- << "Couldn't send packet.\n";
- }
- }
- }
- }
- }
- /**
- * 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();
- }
- }
- /**
- * Returns the current machine's hostname.
- */
- std::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;
- }
- /**
- * 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, true);
- }
- /**
- * Sends over any information about new Collectors that the user code might
- * have recently created.
- */
- void PStatClientImpl::
- report_new_collectors() {
- // Empirically, we determined that you can't send more than about 1400
- // collectors at once without exceeding the 64K limit on a single datagram.
- // So we limit ourselves here to sending only half that many.
- static const int max_collectors_at_once = 700;
- while (_is_connected && _collectors_reported < _client->_num_collectors) {
- PStatClientControlMessage message;
- message._type = PStatClientControlMessage::T_define_collectors;
- int i = 0;
- while (_collectors_reported < _client->_num_collectors &&
- i < max_collectors_at_once) {
- message._collectors.push_back(_client->get_collector_def(_collectors_reported));
- _collectors_reported++;
- i++;
- }
- Datagram datagram;
- message.encode(datagram);
- _writer.send(datagram, _tcp_connection, true);
- }
- }
- /**
- * Sends over any information about new Threads that the user code might have
- * recently created.
- */
- void PStatClientImpl::
- report_new_threads() {
- while (_is_connected && _threads_reported < _client->_num_threads) {
- PStatClientControlMessage message;
- message._type = PStatClientControlMessage::T_define_threads;
- message._first_thread_index = _threads_reported;
- PStatClient::ThreadPointer *threads =
- (PStatClient::ThreadPointer *)_client->_threads;
- while (_threads_reported < _client->_num_threads) {
- message._names.push_back(threads[_threads_reported]->_name);
- _threads_reported++;
- }
- Datagram datagram;
- message.encode(datagram);
- _writer.send(datagram, _tcp_connection, true);
- }
- }
- /**
- * 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";
- }
- }
- /**
- * Called by the internal net code when the connection has been lost.
- */
- void PStatClientImpl::
- connection_reset(const PT(Connection) &connection, bool) {
- if (connection == _tcp_connection) {
- client_disconnect();
- } else if (connection == _udp_connection) {
- pstats_cat.warning()
- << "Trouble sending UDP; switching to TCP only.\n";
- _tcp_count_factor = 0.0f;
- _udp_count_factor = 1.0f;
- } else {
- pstats_cat.warning()
- << "Ignoring spurious connection_reset() message\n";
- }
- }
- #endif // DO_PSTATS
|